<!DOCTYPE html>



  


<html class="theme-next mist use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">



  
  
    
    
  <script src="/myblogs/lib/pace/pace.min.js?v=1.0.2"></script>
  <link href="/myblogs/lib/pace/pace-theme-minimal.min.css?v=1.0.2" rel="stylesheet">







<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
















  
  
  <link href="/myblogs/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />







<link href="/myblogs/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/myblogs/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/myblogs/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/myblogs/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/myblogs/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/myblogs/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Hexo, NexT" />





  <link rel="alternate" href="/myblogs/atom.xml" title="天行健の博客" type="application/atom+xml" />






<meta name="description" content="I do what i do!">
<meta property="og:type" content="website">
<meta property="og:title" content="天行健の博客">
<meta property="og:url" content="https://joy_24.gitee.io/page/3/index.html">
<meta property="og:site_name" content="天行健の博客">
<meta property="og:description" content="I do what i do!">
<meta property="og:locale">
<meta property="article:author" content="Joy Wang">
<meta property="article:tag" content="Java, Spring, Mybatis, SpringMvc, redis">
<meta name="twitter:card" content="summary">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '',
    scheme: 'Mist',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":true,"scrollpercent":true,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://joy_24.gitee.io/page/3/"/>





  <title>天行健の博客</title>
  








<meta name="generator" content="Hexo 5.2.0"></head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left 
  page-home">
    <div class="headband"></div>
    <a target="_blank" rel="noopener" href="https://github.com/JoyWJ" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>


    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/myblogs/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">天行健の博客</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">一个开发博客的初学者。</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/myblogs/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/myblogs/about" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/myblogs/tags" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/myblogs/categories" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/myblogs/archives" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-commonweal">
          <a href="/myblogs/404.html" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-heartbeat"></i> <br />
            
            公益404
          </a>
        </li>
      

      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br />
            
            搜索
          </a>
        </li>
      
    </ul>
  

  
    <div class="site-search">
      
  <div class="popup search-popup local-search-popup">
  <div class="local-search-header clearfix">
    <span class="search-icon">
      <i class="fa fa-search"></i>
    </span>
    <span class="popup-btn-close">
      <i class="fa fa-times-circle"></i>
    </span>
    <div class="local-search-input-wrapper">
      <input autocomplete="off"
             placeholder="搜索..." spellcheck="false"
             type="text" id="local-search-input">
    </div>
  </div>
  <div id="local-search-result"></div>
</div>



    </div>
  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            
  <section id="posts" class="posts-expand">
    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://joy_24.gitee.io/myblogs/2020/11/27/javafx-1/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/myblogs/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="天行健の博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/myblogs/2020/11/27/javafx-1/" itemprop="url">JavaFX桌面应用开发-HelloWorld</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-11-27T14:29:52+08:00">
                2020-11-27
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/myblogs/categories/JavaFx/" itemprop="url" rel="index">
                    <span itemprop="name">JavaFx</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  0
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  1
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            
          
        
      
    </div>
    
    
    
    
    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://joy_24.gitee.io/myblogs/2020/11/27/gradleConfig/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/myblogs/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="天行健の博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/myblogs/2020/11/27/gradleConfig/" itemprop="url">idea里gradle配置和使用</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-11-27T10:03:33+08:00">
                2020-11-27
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/myblogs/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/" itemprop="url" rel="index">
                    <span itemprop="name">中间件</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  1.1k
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  4
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <p>gradle的优点：</p>
<p>1.按约定声明构建和建设；<br>2.强大的支持多工程的构建；<br>3.强大的依赖管理（基于Apache Ivy），提供最大的便利去构建工程；<br>4.全力支持已有的 Maven 或者Ivy仓库基础建设；<br>5.支持传递性依赖管理，在不需要远程仓库和pom.xml和ivy配置文件的前提下；<br>6.基于groovy脚本构建，其build脚本使用groovy语言编写；<br>7.具有广泛的领域模型支持构建；<br>8.深度 API；<br>9.易迁移；<br>10.自由和开放源码，Gradle是一个开源项目，基于 ASL 许可。</p>
<hr>
<h1 id="Windows环境IDEA配置gradle"><a href="#Windows环境IDEA配置gradle" class="headerlink" title="Windows环境IDEA配置gradle"></a>Windows环境IDEA配置gradle</h1><h2 id="配置系统环境变量"><a href="#配置系统环境变量" class="headerlink" title="配置系统环境变量"></a>配置系统环境变量</h2><p>为了使用gradle命令，idea里使用不了命令。比如编译命令<code>gradle build</code></p>
<h2 id="下载"><a href="#下载" class="headerlink" title="下载"></a>下载</h2><p><a target="_blank" rel="noopener" href="https://gradle.org/releases/">gradle安装包下载地址</a>,解压到“<code>D:\soft\gradle\gradle-4.8</code>”</p>
<h2 id="配置环境变量"><a href="#配置环境变量" class="headerlink" title="配置环境变量"></a>配置环境变量</h2><p>打开环境配置，新建系统环境“GRADLE_HOME”,值为<code>D:\soft\gradle\gradle-4.8</code>,找到path变量，后面添加<code>%GRADLE_HOME%\bin;</code>。</p>
<h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><p>在cmd命令里输入<code>gradle -v</code>如果能打出版本号，说明环境配置完毕。</p>
<h2 id="idea配置"><a href="#idea配置" class="headerlink" title="idea配置"></a>idea配置</h2><p>在D盘新建文件<code>.gradle</code>（使用dos命令<code>mkdir .gradle</code> ）,做为gradle下载的jar包仓库主目录，默认在<code>C:\Users\Administrator\.gradle</code>.<br>打开file-&gt;setting-&gt;Build,Execution,Deployment-&gt;Gradle，修改如下：</p>
<p><img src="/myblogs/2020/11/27/gradleConfig/1.png"></p>
<p><code>Gradle home</code>指定了gradle文件目录<br><code>Service directory path</code>指定了gradle工作主目录</p>
<hr>
<h1 id="gradle仓库设置"><a href="#gradle仓库设置" class="headerlink" title="gradle仓库设置"></a>gradle仓库设置</h1><p>建议如下设置</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">repositories &#123;</span><br><span class="line">    mavenLocal()</span><br><span class="line">    maven &#123; url &quot;http:&#x2F;&#x2F;maven.aliyun.com&#x2F;nexus&#x2F;content&#x2F;groups&#x2F;public&#x2F;&quot;&#125;</span><br><span class="line">    mavenCentral()</span><br><span class="line">    jcenter()</span><br><span class="line">    maven &#123; url &quot;https:&#x2F;&#x2F;repo.spring.io&#x2F;snapshot&quot; &#125;</span><br><span class="line">    maven &#123; url &quot;https:&#x2F;&#x2F;repo.spring.io&#x2F;milestone&quot; &#125;</span><br><span class="line">    maven &#123; url &#39;http:&#x2F;&#x2F;oss.jfrog.org&#x2F;artifactory&#x2F;oss-snapshot-local&#x2F;&#39; &#125;  &#x2F;&#x2F;转换pdf使用</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>存储库只是文件的集合，按分组，名称和版本来组织构造。 默认情况下，Gradle不定义任何存储库。 这里使用repositories 指定存储库。<br>1、mavenLocal()：指定使用maven本地仓库，而本地仓库在配置maven时setting文件指定的仓库位置。如<code>&lt;localRepository&gt;D:/repository&lt;/localRepository&gt;</code>，同时将<code>setting</code>文件拷贝到<code>C:\Users\Administrator\.m2</code>目录下，一般该目录下是没有<code>setting</code>文件的，gradle查找jar包顺序如下：<code>gradle默认会按以下顺序去查找本地的仓库：USER_HOME/.m2/settings.xml &gt;&gt; M2_HOME/conf/settings.xml &gt;&gt; USER_HOME/.m2/repository。</code> </p>
<p>2、<code>maven &#123; url &quot;http://maven.aliyun.com/nexus/content/groups/public/&quot;&#125;</code>：指定阿里云镜像加速地址 </p>
<p>3、mavenCentral()：这是Maven的中央仓库，无需配置，直接声明就可以使用 </p>
<p>4、jcenter():JCenter中央仓库，实际也是是用的maven搭建的，但相比Maven仓库更友好，通过CDN分发，并且支持https访问。 </p>
<p>5、后面的<code>maven &#123; url 地址&#125;</code>，指定maven仓库，一般用私有仓库地址或其它的第三方库<br>gradle按配置顺序寻找jar文件。如果本地存在就不会再去下载。不存在的再去maven仓库下载，这里注意下载下来的jar文件不在maven仓库里，而是在gradle的主工作目录下，如上面的<code>D:\.gradle</code>目录 </p>
<p>参考<a target="_blank" rel="noopener" href="http://www.souvc.com/?p=2573">《Gradle实战》如何配置利用Maven本地仓库</a></p>
<p>清理命令</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gradle clean</span><br></pre></td></tr></table></figure>

<ul>
<li>1</li>
</ul>
<p>构建打包命令</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gradle clean build</span><br></pre></td></tr></table></figure>

<h1 id="创建缓存依赖"><a href="#创建缓存依赖" class="headerlink" title="创建缓存依赖"></a>创建缓存依赖</h1><p>执行命令<code>gradle clean build --refresh-dependencies</code>或删除.gradle/caches目录，构建的时候它会下载所有依赖并加入到缓存中。</p>
<hr>
<h1 id="gradle-build构建脚本"><a href="#gradle-build构建脚本" class="headerlink" title="gradle.build构建脚本"></a>gradle.build构建脚本</h1><p>build.gradle是Gradle默认的构建脚本文件，执行Gradle命令的时候，会默认加载当前目录下的build.gradle脚本文件。<br>gradle.build脚本如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">buildScript &#123;</span><br><span class="line">    repositories &#123;</span><br><span class="line">         mavenCentral()</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">repositories &#123;</span><br><span class="line">     mavenCentral()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>buildScript里的repositories是这个脚本需要的依赖库，与项目无关，在执行脚本时，会从这个库里download对应的jar和插件。第二个repositories是项目里需要依赖的jar的库。</p>
<hr>
<h1 id="settings-gradle配置"><a href="#settings-gradle配置" class="headerlink" title="settings.gradle配置"></a>settings.gradle配置</h1><p>是模块Module配置文件，大多数setting.gradle的作用是为了配置子工程，根目录下的settings.gradle脚本文件是针对module的全局配置，它的作用域所包含的所有module是通过settings.gradle来配置。<br>settings.gradle用于创建多Project的Gradle项目。Project在IDEA里对应Module模块。<br>例如配置module名<code>rootProject.name = &#39;DyoonPLM&#39;</code></p>

          
        
      
    </div>
    
    
    
    
    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://joy_24.gitee.io/myblogs/2020/11/26/sort-algorithm/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/myblogs/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="天行健の博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/myblogs/2020/11/26/sort-algorithm/" itemprop="url">程序员必知必会的十大算法</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-11-26T14:43:49+08:00">
                2020-11-26
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/myblogs/categories/%E7%AE%97%E6%B3%95/" itemprop="url" rel="index">
                    <span itemprop="name">算法</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  5.7k
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  23
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h2 id="绪论"><a href="#绪论" class="headerlink" title="绪论"></a>绪论</h2><p>身为程序员，十大排序是是所有合格程序员所必备和掌握的，并且热门的算法比如快排、归并排序还可能问的比较细致，对算法性能和复杂度的掌握有要求。bigsai作为一个负责任的Java和数据结构与算法方向的小博主，在这方    面肯定不能让读者们有所漏洞。跟着本篇走，带你捋一捋常见的十大排序算法，轻轻松松掌握！</p>
<p>首先对于排序来说大多数人对排序的概念停留在冒泡排序或者JDK中的Arrays.sort()，手写各种排序对很多人来说都是一种奢望，更别说十大排序算法了，不过还好你遇到了本篇文章！</p>
<p>对于排序的分类，主要不同的维度比如复杂度来分、内外部、比较非比较等维度来分类。我们正常讲的十大排序算法是内部排序，我们更多将他们分为两大类：基于<strong>「比较和非比较」</strong>这个维度去分排序种类。</p>
<ul>
<li><strong>「非比较类的有桶排序、基数排序、计数排序」</strong>。也有很多人将排序归纳为8大排序，那就是因为基数排序、计数排序是建立在桶排序之上或者是一种特殊的桶排序，但是基数排序和计数排序有它特有的特征，所以在这里就将他们归纳为10种经典排序算法。而比较类排序也可分为</li>
<li>比较类排序也有更细致的分法，有基于交换的、基于插入的、基于选择的、基于归并的，更细致的可以看下面的脑图。</li>
</ul>
<p><img src="/myblogs/2020/11/26/sort-algorithm/1.png" alt="脑图"></p>
<h2 id="交换类"><a href="#交换类" class="headerlink" title="交换类"></a>交换类</h2><h3 id="冒泡排序"><a href="#冒泡排序" class="headerlink" title="冒泡排序"></a>冒泡排序</h3><p>冒泡排序，又称起泡排序，它是一种基于交换的排序典型，也是快排思想的基础，冒泡排序是一种稳定排序算法，时间复杂度为O(n^2).基本思想是：<strong>「循环遍历多次每次从前往后把大元素往后调，每次确定一个最大(最小)元素，多次后达到排序序列。」</strong>(或者从后向前把小元素往前调)。</p>
<p>具体思想为(把大元素往后调)：</p>
<ul>
<li>从第一个元素开始往后遍历，每到一个位置判断是否比后面的元素大，如果比后面元素大，那么就交换两者大小，然后继续向后，这样的话进行一轮之后就可以保证<strong>「最大的那个数被交换交换到最末的位置可以确定」</strong>。</li>
<li>第二次同样从开始起向后判断着前进，如果当前位置比后面一个位置更大的那么就和他后面的那个数交换。但是有点注意的是，这次并不需要判断到最后，只需要判断到倒数第二个位置就行(因为第一次我们已经确定最大的在倒数第一，这次的目的是确定倒数第二)</li>
<li>同理，后面的遍历长度每次减一，直到第一个元素使得整个元素有序。</li>
</ul>
<p>例如<code>2 5 3 1 4</code>排序过程如下：</p>
<p><img src="/myblogs/2020/11/26/sort-algorithm/2.png"></p>
<p>实现代码为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">public void  maopaosort(int[] a) &#123;</span><br><span class="line">  &#x2F;&#x2F; TODO Auto-generated method stub</span><br><span class="line">  for(int i&#x3D;a.length-1;i&gt;&#x3D;0;i--)</span><br><span class="line">  &#123;</span><br><span class="line">    for(int j&#x3D;0;j&lt;i;j++)</span><br><span class="line">    &#123;</span><br><span class="line">      if(a[j]&gt;a[j+1])</span><br><span class="line">      &#123;</span><br><span class="line">        int team&#x3D;a[j];</span><br><span class="line">        a[j]&#x3D;a[j+1];</span><br><span class="line">        a[j+1]&#x3D;team;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="快速排序"><a href="#快速排序" class="headerlink" title="快速排序"></a>快速排序</h3><p>快速排序是对冒泡排序的一种改进，采用递归分治的方法进行求解。而快排相比冒泡是一种不稳定排序,时间复杂度最坏是O(n^2),平均时间复杂度为O(nlogn),最好情况的时间复杂度为O(nlogn)。</p>
<p>对于快排来说，<strong>「基本思想」</strong>是这样的</p>
<ul>
<li>快排需要将序列变成两个部分，就是<strong>「序列左边全部小于一个数」</strong>，<strong>「序列右面全部大于一个数」</strong>，然后利用递归的思想再将左序列当成一个完整的序列再进行排序，同样把序列的右侧也当成一个完整的序列进行排序。</li>
<li>其中这个数在这个序列中是可以随机取的，可以取最左边，可以取最右边，当然也可以取随机数。但是<strong>「通常」</strong>不优化情况我们取最左边的那个数。</li>
</ul>
<p><img src="/myblogs/2020/11/26/sort-algorithm/3.png"></p>
<p>实现代码为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">public void quicksort(int [] a,int left,int right)</span><br><span class="line">&#123;</span><br><span class="line">  int low&#x3D;left;</span><br><span class="line">  int high&#x3D;right;</span><br><span class="line">  &#x2F;&#x2F;下面两句的顺序一定不能混，否则会产生数组越界！！！very important！！！</span><br><span class="line">  if(low&gt;high)&#x2F;&#x2F;作为判断是否截止条件</span><br><span class="line">    return;</span><br><span class="line">  int k&#x3D;a[low];&#x2F;&#x2F;额外空间k，取最左侧的一个作为衡量，最后要求左侧都比它小，右侧都比它大。</span><br><span class="line">  while(low&lt;high)&#x2F;&#x2F;这一轮要求把左侧小于a[low],右侧大于a[low]。</span><br><span class="line">  &#123;</span><br><span class="line">    while(low&lt;high&amp;&amp;a[high]&gt;&#x3D;k)&#x2F;&#x2F;右侧找到第一个小于k的停止</span><br><span class="line">    &#123;</span><br><span class="line">      high--;</span><br><span class="line">    &#125;</span><br><span class="line">    &#x2F;&#x2F;这样就找到第一个比它小的了</span><br><span class="line">    a[low]&#x3D;a[high];&#x2F;&#x2F;放到low位置</span><br><span class="line">    while(low&lt;high&amp;&amp;a[low]&lt;&#x3D;k)&#x2F;&#x2F;在low往右找到第一个大于k的，放到右侧a[high]位置</span><br><span class="line">    &#123;</span><br><span class="line">      low++;</span><br><span class="line">    &#125;</span><br><span class="line">    a[high]&#x3D;a[low];   </span><br><span class="line">  &#125;</span><br><span class="line">  a[low]&#x3D;k;&#x2F;&#x2F;赋值然后左右递归分治求之</span><br><span class="line">  quicksort(a, left, low-1);</span><br><span class="line">  quicksort(a, low+1, right);  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="插入类排序"><a href="#插入类排序" class="headerlink" title="插入类排序"></a>插入类排序</h2><h3 id="直接插入排序"><a href="#直接插入排序" class="headerlink" title="直接插入排序"></a>直接插入排序</h3><p>直接插入排序在所有排序算法中的是最简单排序方式之一。和我们上学时候 从前往后、按高矮顺序排序，那么一堆高低无序的人群中，从第一个开始，如果前面有比自己高的，就直接插入到合适的位置。<strong>「一直到队伍的最后一个完成插入」</strong>整个队列才能满足有序。</p>
<p>直接插入排序遍历比较时间复杂度是每次O(n),交换的时间复杂度每次也是O(n),那么n次总共的时间复杂度就是O(n^2)。有人会问折半(二分)插入能否优化成O(nlogn),答案是不能的。因为二分只能减少查找复杂度每次为O(logn),而插入的时间复杂度每次为O(n)级别，这样总的时间复杂度级别还是O(n^2).</p>
<p>插入排序的具体步骤：</p>
<ul>
<li>选取当前位置(当前位置前面已经有序) 目标就是将当前位置数据插入到前面合适位置。</li>
<li>向前枚举或者二分查找，找到待插入的位置。</li>
<li>移动数组，赋值交换，达到插入效果。</li>
</ul>
<p><img src="/myblogs/2020/11/26/sort-algorithm/4.png"><br>实现代码为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">public void insertsort (int a[])</span><br><span class="line">&#123;</span><br><span class="line">  int team&#x3D;0;</span><br><span class="line">  for(int i&#x3D;1;i&lt;a.length;i++)</span><br><span class="line">  &#123;</span><br><span class="line">    System.out.println(Arrays.toString(a));</span><br><span class="line">    team&#x3D;a[i];</span><br><span class="line">    for(int j&#x3D;i-1;j&gt;&#x3D;0;j--)</span><br><span class="line">    &#123;</span><br><span class="line"></span><br><span class="line">      if(a[j]&gt;team)</span><br><span class="line">      &#123;</span><br><span class="line">        a[j+1]&#x3D;a[j];</span><br><span class="line">        a[j]&#x3D;team; </span><br><span class="line">      &#125; </span><br><span class="line">      else &#123;</span><br><span class="line">        break;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="希尔排序"><a href="#希尔排序" class="headerlink" title="希尔排序"></a>希尔排序</h3><p>直接插入排序因为是O(n^2),在数据量很大或者数据移动位次太多会导致效率太低。很多排序都会想办法拆分序列，然后组合，希尔排序就是以一种特殊的方式进行预处理，考虑到了<strong>「数据量和有序性」</strong>两个方面纬度来设计算法。使得序列前后之间小的尽量在前面，大的尽量在后面，进行若干次的分组别计算，最后一组即是一趟完整的直接插入排序。</p>
<p>对于一个<code>长串</code>，希尔首先将序列分割(非线性分割)而是<strong>「按照某个数模」</strong>(<code>取余</code>这个类似报数1、2、3、4。1、2、3、4)这样形式上在一组的分割先<strong>「各组分别进行直接插入排序」</strong>,这样<strong>「很小的数在后面」</strong>可以通过<strong>「较少的次数移动到相对靠前」</strong>的位置。然后慢慢合并变长，再稍稍移动。</p>
<p>因为每次这样插入都会使得序列变得更加有序，稍微有序序列执行直接插入排序成本并不高。所以这样能够在合并到最终的时候基本小的在前，大的在后，代价越来越小。这样希尔排序相比插入排序还是能节省不少时间的。</p>
<p><img src="/myblogs/2020/11/26/sort-algorithm/5.png"></p>
<p>实现代码为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">public void shellsort (int a[])</span><br><span class="line">&#123;</span><br><span class="line">  int d&#x3D;a.length;</span><br><span class="line">  int team&#x3D;0;&#x2F;&#x2F;临时变量</span><br><span class="line">  for(;d&gt;&#x3D;1;d&#x2F;&#x3D;2)&#x2F;&#x2F;共分成d组</span><br><span class="line">    for(int i&#x3D;d;i&lt;a.length;i++)&#x2F;&#x2F;到那个元素就看这个元素在的那个组即可</span><br><span class="line">    &#123;</span><br><span class="line">      team&#x3D;a[i];</span><br><span class="line">      for(int j&#x3D;i-d;j&gt;&#x3D;0;j-&#x3D;d)</span><br><span class="line">      &#123;    </span><br><span class="line">        if(a[j]&gt;team)</span><br><span class="line">        &#123;</span><br><span class="line">          a[j+d]&#x3D;a[j];</span><br><span class="line">          a[j]&#x3D;team; </span><br><span class="line">        &#125;</span><br><span class="line">        else &#123;</span><br><span class="line">          break;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="选择类排序"><a href="#选择类排序" class="headerlink" title="选择类排序"></a>选择类排序</h2><h3 id="简单选择排序"><a href="#简单选择排序" class="headerlink" title="简单选择排序"></a>简单选择排序</h3><p>简单选择排序（Selection sort）是一种简单直观的排序算法。它的工作原理：首先在未排序序列中找到最小（大）元素，存放到排序序列的起始位置，然后，再从剩余未排序元素中继续寻找最小（大）元素，然后放到<strong>「已排序序列的末尾」</strong>。以此类推，直到所有元素均排序完毕。</p>
<p><img src="/myblogs/2020/11/26/sort-algorithm/6.png"></p>
<p>实现代码为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">public void selectSort(int[] arr) &#123;</span><br><span class="line">  for (int i &#x3D; 0; i &lt; arr.length - 1; i++) &#123;</span><br><span class="line">    int min &#x3D; i; &#x2F;&#x2F; 最小位置</span><br><span class="line">    for (int j &#x3D; i + 1; j &lt; arr.length; j++) &#123;</span><br><span class="line">      if (arr[j] &lt; arr[min]) &#123;</span><br><span class="line">        min &#x3D; j; &#x2F;&#x2F; 更换最小位置</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    if (min !&#x3D; i) &#123;</span><br><span class="line">      swap(arr, i, min); &#x2F;&#x2F; 与第i个位置进行交换</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">private void swap(int[] arr, int i, int j) &#123;</span><br><span class="line">  int temp &#x3D; arr[i];</span><br><span class="line">  arr[i] &#x3D; arr[j];</span><br><span class="line">  arr[j] &#x3D; temp;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="堆排序"><a href="#堆排序" class="headerlink" title="堆排序"></a>堆排序</h3><p>对于堆排序，首先是建立在堆的基础上，堆是一棵完全二叉树，还要先认识下大根堆和小根堆，完全二叉树中所有节点均大于(或小于)它的孩子节点，所以这里就分为两种情况</p>
<ul>
<li>如果所有节点<strong>「大于」</strong>孩子节点值，那么这个堆叫做<strong>「大根堆」</strong>，堆的最大值在根节点。</li>
<li>如果所有节点<strong>「小于」</strong>孩子节点值，那么这个堆叫做<strong>「小根堆」</strong>，堆的最小值在根节点。</li>
</ul>
<p><img src="/myblogs/2020/11/26/sort-algorithm/7.png"></p>
<p>堆排序首先就是<strong>「建堆」</strong>，然后再是调整。对于二叉树(数组表示)，我们从下往上进行调整，从<strong>「第一个非叶子节点」</strong>开始向前调整，对于调整的规则如下：</p>
<p>建堆是一个O(n)的时间复杂度过程，建堆完成后就需要进行删除头排序。给定数组建堆(creatHeap)</p>
<p>①从第一个非叶子节点开始判断交换下移(shiftDown)，使得当前节点和子孩子能够保持堆的性质</p>
<p>②但是普通节点替换可能没问题，对如果交换打破子孩子堆结构性质，那么就要重新下移(shiftDown)被交换的节点一直到停止。</p>
<p><img src="/myblogs/2020/11/26/sort-algorithm/8.png"></p>
<p>堆构造完成，取第一个堆顶元素为最小(最大)，剩下左右孩子依然满足堆的性值，但是缺个堆顶元素，如果给孩子调上来，可能会调动太多并且可能破坏堆结构。</p>
<p>①所以索性把最后一个元素放到第一位。这样只需要判断交换下移(shiftDown）,不过需要注意此时整个堆的大小已经发生了变化，我们在逻辑上不会使用被抛弃的位置，所以在设计函数的时候需要附带一个堆大小的参数。</p>
<p>②重复以上操作，一直堆中所有元素都被取得停止。</p>
<p><img src="/myblogs/2020/11/26/sort-algorithm/9.png"></p>
<p>而堆算法复杂度的分析上，之前建堆时间复杂度是O(n)。而每次删除堆顶然后需要向下交换，每个个数最坏为logn个。这样复杂度就为O(nlogn).总的时间复杂度为O(n)+O(nlogn)=O(nlogn).</p>
<p>实现代码为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line">static void swap(int arr[],int m,int n)</span><br><span class="line">&#123;</span><br><span class="line">  int team&#x3D;arr[m];</span><br><span class="line">  arr[m]&#x3D;arr[n];</span><br><span class="line">  arr[n]&#x3D;team;</span><br><span class="line">&#125;</span><br><span class="line">&#x2F;&#x2F;下移交换 把当前节点有效变换成一个堆(小根)</span><br><span class="line">static void shiftDown(int arr[],int index,int len)&#x2F;&#x2F;0 号位置不用</span><br><span class="line">&#123;</span><br><span class="line">  int leftchild&#x3D;index*2+1;&#x2F;&#x2F;左孩子</span><br><span class="line">  int rightchild&#x3D;index*2+2;&#x2F;&#x2F;右孩子</span><br><span class="line">  if(leftchild&gt;&#x3D;len)</span><br><span class="line">    return;</span><br><span class="line">  else if(rightchild&lt;len&amp;&amp;arr[rightchild]&lt;arr[index]&amp;&amp;arr[rightchild]&lt;arr[leftchild])&#x2F;&#x2F;右孩子在范围内并且应该交换</span><br><span class="line">  &#123;</span><br><span class="line">    swap(arr, index, rightchild);&#x2F;&#x2F;交换节点值</span><br><span class="line">    shiftDown(arr, rightchild, len);&#x2F;&#x2F;可能会对孩子节点的堆有影响，向下重构</span><br><span class="line">  &#125;</span><br><span class="line">  else if(arr[leftchild]&lt;arr[index])&#x2F;&#x2F;交换左孩子</span><br><span class="line">  &#123;</span><br><span class="line">    swap(arr, index, leftchild);</span><br><span class="line">    shiftDown(arr, leftchild, len);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">&#x2F;&#x2F;将数组创建成堆</span><br><span class="line">static void creatHeap(int arr[])</span><br><span class="line">&#123;</span><br><span class="line">  for(int i&#x3D;arr.length&#x2F;2;i&gt;&#x3D;0;i--)</span><br><span class="line">  &#123;</span><br><span class="line">    shiftDown(arr, i,arr.length);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">static void heapSort(int arr[])</span><br><span class="line">&#123;</span><br><span class="line">  System.out.println(&quot;原始数组为         ：&quot;+Arrays.toString(arr));</span><br><span class="line">  int val[]&#x3D;new int[arr.length]; &#x2F;&#x2F;临时储存结果</span><br><span class="line">  &#x2F;&#x2F;step1建堆</span><br><span class="line">  creatHeap(arr);</span><br><span class="line">  System.out.println(&quot;建堆后的序列为  ：&quot;+Arrays.toString(arr));</span><br><span class="line">  &#x2F;&#x2F;step2 进行n次取值建堆，每次取堆顶元素放到val数组中，最终结果即为一个递增排序的序列</span><br><span class="line">  for(int i&#x3D;0;i&lt;arr.length;i++)</span><br><span class="line">  &#123;</span><br><span class="line">    val[i]&#x3D;arr[0];&#x2F;&#x2F;将堆顶放入结果中</span><br><span class="line">    arr[0]&#x3D;arr[arr.length-1-i];&#x2F;&#x2F;删除堆顶元素，将末尾元素放到堆顶</span><br><span class="line">    shiftDown(arr, 0, arr.length-i);&#x2F;&#x2F;将这个堆调整为合法的小根堆，注意(逻辑上的)长度有变化</span><br><span class="line">  &#125;</span><br><span class="line">  &#x2F;&#x2F;数值克隆复制</span><br><span class="line">  for(int i&#x3D;0;i&lt;arr.length;i++)</span><br><span class="line">  &#123;</span><br><span class="line">    arr[i]&#x3D;val[i];</span><br><span class="line">  &#125;</span><br><span class="line">  System.out.println(&quot;堆排序后的序列为:&quot;+Arrays.toString(arr));</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="归并类排序"><a href="#归并类排序" class="headerlink" title="归并类排序"></a>归并类排序</h2><p>在归并类排序一般只讲归并排序，但是归并排序也分二路归并、多路归并，这里就讲较多的二路归并排序，且用递归方式实现。</p>
<h3 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h3><p>归并和快排都是<strong>「基于分治算法」</strong>的，分治算法其实应用挺多的，很多分治会用到递归，但事实上<strong>「分治和递归是两把事」</strong>。分治就是分而治之，可以采用递归实现，也可以自己遍历实现非递归方式。而归并排序就是先将问题分解成代价较小的子问题，子问题再采取代价较小的合并方式完成一个排序。</p>
<p>至于归并的思想是这样的：</p>
<ul>
<li>第一次：整串先进行划分成一个一个单独，第一次是将序列中(<code>1 2 3 4 5 6---</code>)两两归并成有序，归并完(<code>xx xx xx xx----</code>)这样局部有序的序列。</li>
<li>第二次就是两两归并成若干四个(<code>1 2 3 4 5 6 7 8 ----</code>)<strong>「每个小局部是有序的」</strong>。</li>
<li>就这样一直到最后这个串串只剩一个，然而这个耗费的总次数logn。每次操作的时间复杂的又是<code>O(n)</code>。所以总共的时间复杂度为<code>O(nlogn)</code>.</li>
</ul>
<p><img src="/myblogs/2020/11/26/sort-algorithm/10.png"></p>
<p>合并为一个O(n)的过程：</p>
<p><img src="/myblogs/2020/11/26/sort-algorithm/11.png"></p>
<p>实现代码为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">private static void mergesort(int[] array, int left, int right) &#123;</span><br><span class="line">  int mid&#x3D;(left+right)&#x2F;2;</span><br><span class="line">  if(left&lt;right)</span><br><span class="line">  &#123;</span><br><span class="line">    mergesort(array, left, mid);</span><br><span class="line">    mergesort(array, mid+1, right);</span><br><span class="line">    merge(array, left,mid, right);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">private static void merge(int[] array, int l, int mid, int r) &#123;</span><br><span class="line">  int lindex&#x3D;l;int rindex&#x3D;mid+1;</span><br><span class="line">  int team[]&#x3D;new int[r-l+1];</span><br><span class="line">  int teamindex&#x3D;0;</span><br><span class="line">  while (lindex&lt;&#x3D;mid&amp;&amp;rindex&lt;&#x3D;r) &#123;&#x2F;&#x2F;先左右比较合并</span><br><span class="line">    if(array[lindex]&lt;&#x3D;array[rindex])</span><br><span class="line">    &#123;</span><br><span class="line">      team[teamindex++]&#x3D;array[lindex++];</span><br><span class="line">    &#125;</span><br><span class="line">    else &#123;    </span><br><span class="line">      team[teamindex++]&#x3D;array[rindex++];</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  while(lindex&lt;&#x3D;mid)&#x2F;&#x2F;当一个越界后剩余按序列添加即可</span><br><span class="line">  &#123;</span><br><span class="line">    team[teamindex++]&#x3D;array[lindex++];</span><br><span class="line"></span><br><span class="line">  &#125;</span><br><span class="line">  while(rindex&lt;&#x3D;r)</span><br><span class="line">  &#123;</span><br><span class="line">    team[teamindex++]&#x3D;array[rindex++];</span><br><span class="line">  &#125; </span><br><span class="line">  for(int i&#x3D;0;i&lt;teamindex;i++)</span><br><span class="line">  &#123;</span><br><span class="line">    array[l+i]&#x3D;team[i];</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="桶类排序"><a href="#桶类排序" class="headerlink" title="桶类排序"></a>桶类排序</h2><h3 id="桶排序"><a href="#桶排序" class="headerlink" title="桶排序"></a>桶排序</h3><p>桶排序是一种用空间换取时间的排序，桶排序重要的是它的思想，而不是具体实现，时间复杂度最好可能是线性O(n)，桶排序不是基于比较的排序而是一种分配式的。桶排序从字面的意思上看：</p>
<ul>
<li>桶：若干个桶，说明此类排序将数据放入若干个桶中。</li>
<li>桶：每个桶有容量，桶是有一定容积的容器，所以每个桶中可能有多个元素。</li>
<li>桶：从整体来看，整个排序更希望桶能够更匀称，即既不溢出(太多)又不太少。</li>
</ul>
<p>桶排序的思想为：<strong>「将待排序的序列分到若干个桶中，每个桶内的元素再进行个别排序。」</strong> 当然桶排序选择的方案跟具体的数据有关系，桶排序是一个比较广泛的概念，并且计数排序是一种特殊的桶排序，基数排序也是建立在桶排序的基础上。在数据分布均匀且每个桶元素趋近一个时间复杂度能达到O(n),但是如果数据范围较大且相对集中就不太适合使用桶排序。</p>
<p><img src="/myblogs/2020/11/26/sort-algorithm/12.jpg"></p>
<p>实现一个简单桶排序：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.List;</span><br><span class="line">&#x2F;&#x2F;微信公众号：bigsai</span><br><span class="line">public class bucketSort &#123;</span><br><span class="line"> public static void main(String[] args) &#123;</span><br><span class="line">  int a[]&#x3D; &#123;1,8,7,44,42,46,38,34,33,17,15,16,27,28,24&#125;;</span><br><span class="line">  List[] buckets&#x3D;new ArrayList[5];</span><br><span class="line">  for(int i&#x3D;0;i&lt;buckets.length;i++)&#x2F;&#x2F;初始化</span><br><span class="line">  &#123;</span><br><span class="line">   buckets[i]&#x3D;new ArrayList&lt;Integer&gt;();</span><br><span class="line">  &#125;</span><br><span class="line">  for(int i&#x3D;0;i&lt;a.length;i++)&#x2F;&#x2F;将待排序序列放入对应桶中</span><br><span class="line">  &#123;</span><br><span class="line">   int index&#x3D;a[i]&#x2F;10;&#x2F;&#x2F;对应的桶号</span><br><span class="line">   buckets[index].add(a[i]);</span><br><span class="line">  &#125;</span><br><span class="line">  for(int i&#x3D;0;i&lt;buckets.length;i++)&#x2F;&#x2F;每个桶内进行排序(使用系统自带快排)</span><br><span class="line">  &#123;</span><br><span class="line">   buckets[i].sort(null);</span><br><span class="line">   for(int j&#x3D;0;j&lt;buckets[i].size();j++)&#x2F;&#x2F;顺便打印输出</span><br><span class="line">   &#123;</span><br><span class="line">    System.out.print(buckets[i].get(j)+&quot; &quot;);</span><br><span class="line">   &#125;</span><br><span class="line">  &#125; </span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="计数排序"><a href="#计数排序" class="headerlink" title="计数排序"></a>计数排序</h3><p>计数排序是一种特殊的桶排序，每个桶的大小为1，每个桶不在用List表示，而通常用一个值用来计数。</p>
<p>在<strong>「设计具体算法的时候」</strong>，先找到最小值min，再找最大值max。然后创建这个区间大小的数组,从min的位置开始计数，这样就可以最大程度的压缩空间，提高空间的使用效率。</p>
<p><img src="/myblogs/2020/11/26/sort-algorithm/13.png"></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">public static void countSort(int a[])</span><br><span class="line">&#123;</span><br><span class="line">  int min&#x3D;Integer.MAX_VALUE;int max&#x3D;Integer.MIN_VALUE;</span><br><span class="line">  for(int i&#x3D;0;i&lt;a.length;i++)&#x2F;&#x2F;找到max和min</span><br><span class="line">  &#123;</span><br><span class="line">    if(a[i]&lt;min) </span><br><span class="line">      min&#x3D;a[i];</span><br><span class="line">    if(a[i]&gt;max)</span><br><span class="line">      max&#x3D;a[i];</span><br><span class="line">  &#125;</span><br><span class="line">  int count[]&#x3D;new int[max-min+1];&#x2F;&#x2F;对元素进行计数</span><br><span class="line">  for(int i&#x3D;0;i&lt;a.length;i++)</span><br><span class="line">  &#123;</span><br><span class="line">    count[a[i]-min]++;</span><br><span class="line">  &#125;</span><br><span class="line">  &#x2F;&#x2F;排序取值</span><br><span class="line">  int index&#x3D;0;</span><br><span class="line">  for(int i&#x3D;0;i&lt;count.length;i++)</span><br><span class="line">  &#123;</span><br><span class="line">    while (count[i]--&gt;0) &#123;</span><br><span class="line">      a[index++]&#x3D;i+min;&#x2F;&#x2F;有min才是真正值</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="基数排序"><a href="#基数排序" class="headerlink" title="基数排序"></a>基数排序</h3><p>基数排序是一种很容易理解但是比较难实现(优化)的算法。基数排序也称为卡片排序，基数排序的原理就是多次利用计数排序(计数排序是一种特殊的桶排序)，但是和前面的普通桶排序和计数排序有所区别的是，<strong>「基数排序并不是将一个整体分配到一个桶中」</strong>，而是将自身拆分成一个个组成的元素，每个元素分别顺序分配放入桶中、顺序收集，当从前往后或者从后往前每个位置都进行过这样顺序的分配、收集后，就获得了一个有序的数列。</p>
<p><img src="/myblogs/2020/11/26/sort-algorithm/14.jpg"></p>
<p>如果是数字类型排序，那么这个桶只需要装0-9大小的数字，但是如果是字符类型，那么就需要注意ASCII的范围。</p>
<p>所以遇到这种情况我们基数排序思想很简单，就拿 934，241，3366，4399这几个数字进行基数排序的一趟过程来看，第一次会根据各位进行分配、收集：</p>
<p><img src="/myblogs/2020/11/26/sort-algorithm/15.jpg"></p>
<p>分配和收集都是有序的，第二次会根据十位进行分配、收集，此次是在第一次个位分配、收集基础上进行的，所以所有数字单看个位十位是有序的。</p>
<p><img src="/myblogs/2020/11/26/sort-algorithm/16.jpg"></p>
<p>而第三次就是对百位进行分配收集，此次完成之后百位及其以下是有序的。</p>
<p><img src="/myblogs/2020/11/26/sort-algorithm/17.jpg"></p>
<p>而最后一次的时候进行处理的时候，千位有的数字需要补零，这次完毕后后千位及以后都有序，即整个序列排序完成。</p>
<p><img src="/myblogs/2020/11/26/sort-algorithm/18.jpg"></p>
<p>简单实现代码为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">static void radixSort(int[] arr)&#x2F;&#x2F;int 类型 从右往左</span><br><span class="line">&#123;</span><br><span class="line">  List&lt;Integer&gt;bucket[]&#x3D;new ArrayList[10];</span><br><span class="line">  for(int i&#x3D;0;i&lt;10;i++)</span><br><span class="line">  &#123;</span><br><span class="line">    bucket[i]&#x3D;new ArrayList&lt;Integer&gt;();</span><br><span class="line">  &#125;</span><br><span class="line">  &#x2F;&#x2F;找到最大值</span><br><span class="line">  int max&#x3D;0;&#x2F;&#x2F;假设都是正数</span><br><span class="line">  for(int i&#x3D;0;i&lt;arr.length;i++)</span><br><span class="line">  &#123;</span><br><span class="line">    if(arr[i]&gt;max)</span><br><span class="line">      max&#x3D;arr[i];</span><br><span class="line">  &#125;</span><br><span class="line">  int divideNum&#x3D;1;&#x2F;&#x2F;1 10 100 100……用来求对应位的数字</span><br><span class="line">  while (max&gt;0) &#123;&#x2F;&#x2F;max 和num 控制</span><br><span class="line">    for(int num:arr)</span><br><span class="line">    &#123;</span><br><span class="line">      bucket[(num&#x2F;divideNum)%10].add(num);&#x2F;&#x2F;分配 将对应位置的数字放到对应bucket中</span><br><span class="line">    &#125;</span><br><span class="line">    divideNum*&#x3D;10;</span><br><span class="line">    max&#x2F;&#x3D;10;</span><br><span class="line">    int idx&#x3D;0;</span><br><span class="line">    &#x2F;&#x2F;收集 重新捡起数据</span><br><span class="line">    for(List&lt;Integer&gt;list:bucket)</span><br><span class="line">    &#123;</span><br><span class="line">      for(int num:list)</span><br><span class="line">      &#123;</span><br><span class="line">        arr[idx++]&#x3D;num;</span><br><span class="line">      &#125;</span><br><span class="line">      list.clear();&#x2F;&#x2F;收集完需要清空留下次继续使用</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>当然，基数排序还有字符串等长、不等长、一维数组优化等各种实现需要需学习，具体可以参考公众号内其他文章。</p>
<h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>本次十大排序就这么潇洒的过了一遍，我想大家都应该有所领悟了吧！对于算法总结，避免不必要的劳动力，我分享这个表格给大家：</p>
<table>
<thead>
<tr>
<th align="left">排序算法</th>
<th align="left">平均时间复杂度</th>
<th align="left">最好</th>
<th align="left">最坏</th>
<th align="left">空间复杂度</th>
<th align="left">稳定性</th>
</tr>
</thead>
<tbody><tr>
<td align="left">冒泡排序</td>
<td align="left">O(n^2)</td>
<td align="left">O(n)</td>
<td align="left">O(n^2)</td>
<td align="left">O(1)</td>
<td align="left">稳定</td>
</tr>
<tr>
<td align="left">快速排序</td>
<td align="left">O(nlogn)</td>
<td align="left">O(nlogn)</td>
<td align="left">O(n^2)</td>
<td align="left">O(logn)</td>
<td align="left">不稳定</td>
</tr>
<tr>
<td align="left">插入排序</td>
<td align="left">O(n^2)</td>
<td align="left">O(n)</td>
<td align="left">O(n^2)</td>
<td align="left">O(1)</td>
<td align="left">稳定</td>
</tr>
<tr>
<td align="left">希尔排序</td>
<td align="left">O(n^1.3)</td>
<td align="left">O(n)</td>
<td align="left">O(nlog2n)</td>
<td align="left">O(1)</td>
<td align="left">不稳定</td>
</tr>
<tr>
<td align="left">选择排序</td>
<td align="left">O(n^2)</td>
<td align="left">O(n^2)</td>
<td align="left">O(n^2)</td>
<td align="left">O(1)</td>
<td align="left">不稳定</td>
</tr>
<tr>
<td align="left">堆排序</td>
<td align="left">O(nlogn)</td>
<td align="left">O(nlogn)</td>
<td align="left">O(nlogn)</td>
<td align="left">O(1)</td>
<td align="left">不稳定</td>
</tr>
<tr>
<td align="left">归并排序</td>
<td align="left">O(nlogn)</td>
<td align="left">O(nlogn)</td>
<td align="left">O(nlogn)</td>
<td align="left">O(n)</td>
<td align="left">稳定</td>
</tr>
<tr>
<td align="left">桶排序</td>
<td align="left">O(n+k)</td>
<td align="left">O(n+k)</td>
<td align="left">O(n+k)</td>
<td align="left">O(n+k)</td>
<td align="left">稳定</td>
</tr>
<tr>
<td align="left">计数排序</td>
<td align="left">O(n+k)</td>
<td align="left">O(n+k)</td>
<td align="left">O(n+k)</td>
<td align="left">O(k)</td>
<td align="left">稳定</td>
</tr>
<tr>
<td align="left">基数排序</td>
<td align="left">O(n*k)</td>
<td align="left">O(n*k)</td>
<td align="left">O(n*k)</td>
<td align="left">O(n+k)</td>
<td align="left">稳定</td>
</tr>
</tbody></table>
<h2 id="END"><a href="#END" class="headerlink" title="END"></a>END</h2>
          
        
      
    </div>
    
    
    
    
    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://joy_24.gitee.io/myblogs/2020/11/26/idea-hotKey-1/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/myblogs/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="天行健の博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/myblogs/2020/11/26/idea-hotKey-1/" itemprop="url">idea 这样配置，快到飞起</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-11-26T14:33:54+08:00">
                2020-11-26
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/myblogs/categories/IDE/" itemprop="url" rel="index">
                    <span itemprop="name">IDE</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  3k
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  12
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h1 id="1-设置maven"><a href="#1-设置maven" class="headerlink" title="1.设置maven"></a>1.设置maven</h1><blockquote>
<ol>
<li>在File-&gt;settings-&gt;搜索maven</li>
<li>Mavan home directory–设置maven安装包的bin文件夹所在的位置</li>
<li>User settings file–设置setting文件所在的位置</li>
<li>Local repository–设置本地仓库的</li>
</ol>
</blockquote>
<h1 id="2-IDEA-设置代码行宽度"><a href="#2-IDEA-设置代码行宽度" class="headerlink" title="2.IDEA 设置代码行宽度"></a>2.IDEA 设置代码行宽度</h1><blockquote>
<ol>
<li>在File-&gt;settings-&gt;Editor-&gt;CodeStyle</li>
<li>有人会问，如果输入的代码超出宽度界线时，如何让IDE自动将代码换行？有两种方式！</li>
<li>第一种，在上述的“Right margin (columns)”的下方，有“Wrap when typing reaches right margin”选项，选中它，是什么效果呢？</li>
<li>随着输入的字符的增加，当代码宽度到达界线时，IDEA会自动将代码换行。</li>
<li>第一种方式是在输入代码时触发，还有第二种方式，在File-&gt;settings-&gt;CodeStyle-&gt;Java中，选中“Wrapping and Braces”选项卡，</li>
<li>在“Keep when reformatting”中有一个“Ensure rigth margin is not exceeded”，选中它，是什么效果呢？</li>
<li>从配置项的字面意思很容易理解，在格式化Java代码时，确保代码没有超过宽度界线。</li>
<li>即输入的代码超出界线后，</li>
</ol>
</blockquote>
<h1 id="3-IDEA-提示不区分大小写"><a href="#3-IDEA-提示不区分大小写" class="headerlink" title="3.IDEA 提示不区分大小写"></a>3.IDEA 提示不区分大小写</h1><blockquote>
<ol>
<li>首先打开File—–&gt;setting</li>
<li>然后，输入：sensitive</li>
<li>将右侧的case sensitive completion 修改为NONE</li>
</ol>
</blockquote>
<h1 id="4-IntelliJ强制更新Maven-Dependencies"><a href="#4-IntelliJ强制更新Maven-Dependencies" class="headerlink" title="4.IntelliJ强制更新Maven Dependencies"></a>4.IntelliJ强制更新Maven Dependencies</h1><blockquote>
<p>1.Intellj自动载入Mave依赖的功能很好用，但有时候会碰到问题，导致pom文件修改却没有触发自动重新载入的动作，此时需要手动强制更新依赖。</p>
<p>2.如下：</p>
<ol>
<li>手动删除ProjectSettings里面的Libraries内容；</li>
<li>在MavenProject的试图里clean一下，删除之前编译过的文件；</li>
<li>项目右键-》Maven-》Reimport</li>
<li>Ok，此时发现依赖已经建立！</li>
</ol>
</blockquote>
<h1 id="5-idea的环境配置默认保存位置"><a href="#5-idea的环境配置默认保存位置" class="headerlink" title="5.idea的环境配置默认保存位置"></a>5.idea的环境配置默认保存位置</h1><blockquote>
<ol>
<li>idea的环境配置默认保存位置:C:\Users\xxxxxxxxx.IntelliJIdea14,xxxxxx代表用户目录,</li>
<li>可以对该目录进行备份,一但环境出问题恢复此配置即可.</li>
<li>可以在%IDEA_HOME%/bin/idea.properties中修改该配置路径.</li>
</ol>
</blockquote>
<h1 id="6-隐藏不想看到的文件或者文件夹（类似eclipse的filter功能）"><a href="#6-隐藏不想看到的文件或者文件夹（类似eclipse的filter功能）" class="headerlink" title="6.隐藏不想看到的文件或者文件夹（类似eclipse的filter功能）"></a><code>6.隐藏不想看到的文件或者文件夹（类似eclipse的filter功能）</code></h1><blockquote>
<ol>
<li>intellij idea 隐藏不想看到的文件或者文件夹（类似eclipse的filter功能）</li>
<li>打开intellij –&gt;:&gt;File–&gt;&gt;Settings–&gt;&gt;搜索FileTypes</li>
</ol>
</blockquote>
<h1 id="7-修改为Eclipse快捷键"><a href="#7-修改为Eclipse快捷键" class="headerlink" title="7.修改为Eclipse快捷键"></a>7.修改为Eclipse快捷键</h1><blockquote>
<ol>
<li>File-&gt;Settings-&gt;Keymap=&gt;Keymaps改为Eclipse copy</li>
</ol>
</blockquote>
<h1 id="8-修改默认设置–default-setting"><a href="#8-修改默认设置–default-setting" class="headerlink" title="8.修改默认设置–default setting"></a>8.修改默认设置–default setting</h1><blockquote>
<ol>
<li>修改默认设置–default setting</li>
</ol>
</blockquote>
<h1 id="9-修改智能提示快捷键"><a href="#9-修改智能提示快捷键" class="headerlink" title="9.修改智能提示快捷键"></a>9.修改智能提示快捷键</h1><blockquote>
<ol>
<li>File-&gt;Settings-&gt;Keymap-&gt;Main menu -&gt;Code-&gt;Completion-&gt;Basic=&gt;修改为Ctrl+Alt+Enter</li>
<li>保存时把冲突的Remove掉。</li>
<li>File-&gt;Settings-&gt;Keymap-&gt;EditorActions-&gt;CompleteCurrentStatement=&gt;修改为Ctrl+;</li>
</ol>
</blockquote>
<h1 id="10-查找快捷键冲突问题处理"><a href="#10-查找快捷键冲突问题处理" class="headerlink" title="10.查找快捷键冲突问题处理"></a>10.查找快捷键冲突问题处理</h1><blockquote>
<ol>
<li>File-&gt;Settings-&gt;Keymap-&gt;Main menu -&gt;Edit-&gt;Find=&gt;修改Find…和Replace…分别改为Ctrl+F 和Ctrl+R</li>
</ol>
</blockquote>
<h1 id="11-显示行号"><a href="#11-显示行号" class="headerlink" title="11.显示行号"></a>11.显示行号</h1><blockquote>
<ol>
<li>File-&gt;Settings-&gt;Editor-&gt;General-&gt;Appearance=&gt;Show line numbers选中</li>
</ol>
</blockquote>
<h1 id="12-代码智能提示，忽略大小写"><a href="#12-代码智能提示，忽略大小写" class="headerlink" title="12.代码智能提示，忽略大小写"></a>12.代码智能提示，忽略大小写</h1><blockquote>
<ol>
<li>File-&gt;Settings -&gt; Editor-&gt;CodeCompletion里把Case sensitive completion设置为None就可以了</li>
</ol>
</blockquote>
<h1 id="13-用-标识编辑过的文件"><a href="#13-用-标识编辑过的文件" class="headerlink" title="13.用*标识编辑过的文件"></a>13.用*标识编辑过的文件</h1><blockquote>
<ol>
<li>Editor–&gt;General–&gt;EditorTabs</li>
<li>在IDEA中，你需要做以下设置,这样被修改的文件会以*号标识出来，你可以及时保存相关的文件。</li>
<li>“Mark modifyied tabs with asterisk”</li>
</ol>
</blockquote>
<h1 id="14-关闭自动代码提示"><a href="#14-关闭自动代码提示" class="headerlink" title="14.关闭自动代码提示"></a>14.关闭自动代码提示</h1><blockquote>
<ol>
<li>Preferences=&gt; IDE Settings=&gt;Editor=&gt;CodeCompletion=&gt;Autopopup documentation in (ms)</li>
</ol>
</blockquote>
<h1 id="15-常用快捷键"><a href="#15-常用快捷键" class="headerlink" title="15.常用快捷键"></a>15.常用快捷键</h1><blockquote>
<ol>
<li>ØTop#10切来切去：Ctrl+Tab</li>
<li>ØTop#9选你所想【选中上下文相关联代码】：Ctrl+W</li>
<li>ØTop#8代码生成：Template/Postfix+Tab</li>
<li>ØTop#7发号施令：Ctrl+Shift+A</li>
<li>ØTop#6无处藏身：Shift+Shift</li>
<li>ØTop#5自动完成：Ctrl+Shift+Enter</li>
<li>ØTop#4创造万物：Alt+Insert</li>
</ol>
<p>使用前三名！</p>
<ol>
<li>ØTop#1智能补全：Ctrl+Shift+Space</li>
<li>ØTop#1自我修复：Alt+Enter</li>
<li>ØTop#1重构一切：Ctrl+Shift+Alt+T</li>
</ol>
<p>其他辅助</p>
<ol>
<li>以上这些神键配上一些辅助快捷键，即可让你的双手90%以上的时间摆脱鼠标，专注于键盘仿佛在进行钢琴表演。这些不起眼却是至关重要的最后一块拼图有：</li>
<li>Ø命令：Ctrl+Shift+A可以查找所有Intellij的命令，并且每个命令后面还有其快捷键。所以它不仅是一大神键，也是查找学习快捷键的工具。</li>
<li>Ø新建：Alt+Insert可以新建类、方法等任何东西。</li>
<li>Ø格式化代码：格式化import列表Ctrl+Alt+O，格式化代码Ctrl+Alt+L。</li>
<li>Ø切换窗口：Alt+Num，常用的有1-项目结构，3-搜索结果，4/5-运行调试。Ctrl+Tab切换标签页，Ctrl+E/Ctrl+Shift+E打开最近打开过的或编辑过的文件。</li>
<li>Ø单元测试：Ctrl+Alt+T创建单元测试用例。</li>
<li>Ø运行：Alt+Shift+F10运行程序，Shift+F9启动调试，Ctrl+F2停止。</li>
<li>Ø调试：F7/F8/F9分别对应Step into，Step over，Continue。</li>
<li>此外还有些我自定义的，例如水平分屏Ctrl+|等，和一些神奇的小功能Ctrl+Shift+V粘贴很早以前拷贝过的，Alt+Shift+Insert(块选)进入到列模式进行按列选中</li>
</ol>
</blockquote>
<h1 id="16-svn-不能同步代码问题修正"><a href="#16-svn-不能同步代码问题修正" class="headerlink" title="16.svn 不能同步代码问题修正"></a>16.svn 不能同步代码问题修正</h1><blockquote>
<p>File-&gt;Settings-&gt;Subversion-&gt;General=&gt;Use command line client 选中</p>
<ol>
<li>使用command line方式需要指定svn.exe的路径,例如:D:\tools\TortoiseSVN\bin\svn.exe</li>
<li>注意,安装TortoiseSVN时路径中不要带空格,例如:C:\Program Files\TortoiseSVN\bin\svn.exe就会报错.</li>
<li>安装TortoiseSVN选择全部安装组件,否则可能没有svn.exe</li>
</ol>
</blockquote>
<h1 id="17-设置idea的SVN忽略掉-iml文件"><a href="#17-设置idea的SVN忽略掉-iml文件" class="headerlink" title="17.设置idea的SVN忽略掉*.iml文件"></a>17.设置idea的SVN忽略掉*.iml文件</h1><blockquote>
<ol>
<li>Editor-&gt;FileTypes=&gt;Ignore files and folders增加*.iml;</li>
<li>在lgnore files and folesrs中输入.idea;注意要”;”结尾。你就可以隐藏.idea文件夹了</li>
</ol>
</blockquote>
<h1 id="18-改变编辑文本字体大小"><a href="#18-改变编辑文本字体大小" class="headerlink" title="18.改变编辑文本字体大小"></a>18.改变编辑文本字体大小</h1><blockquote>
<ol>
<li>File-&gt; settings -&gt; EDITOR COLORS &amp; FONTS -&gt; FONT -&gt; SIZE</li>
</ol>
</blockquote>
<h1 id="19-IDEA编码设置"><a href="#19-IDEA编码设置" class="headerlink" title="19.IDEA编码设置"></a>19.IDEA编码设置</h1><blockquote>
<ol>
<li>FILE -&gt; SETTINGS -&gt; FILE ENCODINGS =&gt; IDE ENCODING</li>
<li>FILE -&gt; SETTINGS -&gt; FILE ENCODINGS =&gt;ProjectEncoding</li>
<li>FILE -&gt; SETTINGS -&gt; FILE ENCODINGS =&gt;Default encoding for properties files</li>
<li>FILE -&gt; SETTINGS -&gt; FILE ENCODINGS =&gt;Transparentnative-to-ascii conversion</li>
</ol>
</blockquote>
<h1 id="20-Live-Templates"><a href="#20-Live-Templates" class="headerlink" title="20.Live Templates"></a>20.Live Templates</h1><blockquote>
<p>System.out.println 快捷输出</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">“abc”.sout &#x3D;&gt;System.out.println(&quot;abc&quot;);</span><br></pre></td></tr></table></figure>

<p>在eclipse中使用方式为： <code>sysout=&gt;System.out.println();</code></p>
</blockquote>
<blockquote>
<p>for循环</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">List&lt;String&gt; list &#x3D;newArrayList&lt;String&gt;();</span><br></pre></td></tr></table></figure>

<p>输入: list.for即可输出</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">for(String s:list)&#123;&#96;&#96;&#125;</span><br></pre></td></tr></table></figure>
</blockquote>
<h1 id="21-配置tomcat参数"><a href="#21-配置tomcat参数" class="headerlink" title="21.配置tomcat参数"></a>21.配置tomcat参数</h1><blockquote>
<ol>
<li>vm options:-Xms256m-Xmx512m-XX:PermSize=128m-XX:MaxPermSize=256m</li>
</ol>
</blockquote>
<h1 id="22-idea安装插件的方法"><a href="#22-idea安装插件的方法" class="headerlink" title="22.idea安装插件的方法"></a>22.idea安装插件的方法</h1><blockquote>
<ol>
<li>以IntelliJ IDEA 14.0.1安装findbugs插件为例：</li>
<li>(1)在线方式:进入File-&gt;setting-&gt;plugins-&gt;browse repositorits 搜索你要下载的插件名称，</li>
<li>右侧可以找到下载地址,完成后按提示重启即可.</li>
<li>(2)离线安装:下载findbugs插件地址：<a target="_blank" rel="noopener" href="http://plugins.jetbrains.com/plugin/3847">http://plugins.jetbrains.com/plugin/3847</a>,</li>
<li>将下载的FindBugs-IDEA-0.9.994.zip,安装插件：进入File-&gt;setting-&gt;plugins=&gt; Install plugin from disk…</li>
<li>定位到到刚才下载的jar,点击ok,完成后按提示重启即可.</li>
<li>插件安装的位置在C:\Users\xxxxxxxxx.IntelliJIdea14\config\plugins\插件名下.</li>
<li>安装iBATIS/MyBatis min-plugin插件</li>
</ol>
</blockquote>
<h1 id="23-调整idea启动时的内存配置参数"><a href="#23-调整idea启动时的内存配置参数" class="headerlink" title="23.调整idea启动时的内存配置参数"></a>23.调整idea启动时的内存配置参数</h1><blockquote>
<ol>
<li>%IDEA_HOME%/bin/idea.exe.vmoptions</li>
</ol>
</blockquote>
<h1 id="24-导入eclipse-web项目发布到Tomcat如果找不到"><a href="#24-导入eclipse-web项目发布到Tomcat如果找不到" class="headerlink" title="24.导入eclipse web项目发布到Tomcat如果找不到"></a>24.导入eclipse web项目发布到Tomcat如果找不到</h1><blockquote>
<ol>
<li>导入eclipse web项目发布到Tomcat如果找不到,可以在环境配置的Facets增加web支持,在Artifacts中增加项目部署模块名</li>
</ol>
</blockquote>
<h1 id="25-每次打开一个新jsp或java文件时-cpu都占用很高-去掉检验即可"><a href="#25-每次打开一个新jsp或java文件时-cpu都占用很高-去掉检验即可" class="headerlink" title="25.每次打开一个新jsp或java文件时,cpu都占用很高,去掉检验即可"></a>25.每次打开一个新jsp或java文件时,cpu都占用很高,去掉检验即可</h1><blockquote>
<p>每次打开一个新jsp或java文件时,cpu都占用很高,去掉检验即可:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">file-&gt;settings-&gt;editor-&gt;inspections</span><br></pre></td></tr></table></figure>
</blockquote>
<h1 id="26-idea增加spring-struts关联文件支持"><a href="#26-idea增加spring-struts关联文件支持" class="headerlink" title="26.idea增加spring/struts关联文件支持"></a>26.idea增加spring/struts关联文件支持</h1><blockquote>
<ol>
<li>project Settings-&gt;Modules-&gt;选中项目右键可添加</li>
</ol>
</blockquote>
<h1 id="27-IDEA开启类修改后自动编译"><a href="#27-IDEA开启类修改后自动编译" class="headerlink" title="27. IDEA开启类修改后自动编译"></a>27. IDEA开启类修改后自动编译</h1><blockquote>
<ol>
<li>File-&gt;setting-&gt;Buil,Execution,Deployment-&gt;compiler=&gt;Make project automatically</li>
<li>编译错误问题解决</li>
<li>Error:java:Compilation failed: internal java compiler error</li>
<li>set中Java complier 设置的问题，项目中有人用jdk1.6有人用jdk1.7版本不一样会一起这个错误</li>
</ol>
</blockquote>
<h1 id="28-提示实现Serializable接口"><a href="#28-提示实现Serializable接口" class="headerlink" title="28.提示实现Serializable接口"></a>28.提示实现Serializable接口</h1><blockquote>
<ol>
<li>使用Eclipse或MyEclipse的同学可能知道，如果implementsSerializable接口时，会提示你生成 serialVersionUID。</li>
<li>但Intellij IDEA 默认没启用这个功能。</li>
<li>Preferences-&gt;IEditor-&gt;nspections-&gt;Serialization issues-&gt;Serializableclass without ’serialVersionUID’，</li>
<li>选中以上后，在你的class中：光标定位在类名前，按Alt+Enter就会提示自动创建 serialVersionUID了</li>
</ol>
</blockquote>
<h1 id="29-演出模式"><a href="#29-演出模式" class="headerlink" title="29.演出模式"></a>29.演出模式</h1><blockquote>
<p>我们可以使用【Presentation Mode】，将IDEA弄到最大，可以让你只关注一个类里面的代码，进行毫无干扰的coding。</p>
<p>可以使用Alt+V快捷键，谈出View视图，然后选择Enter Presentation Mode。效果如下：</p>
</blockquote>
<p><img src="/myblogs/2020/11/26/idea-hotKey-1/0.jpg" alt="img"></p>
<blockquote>
<p>这个模式的好处就是，可以让你更加专注，因为你只能看到特定某个类的代码。可能读者会问，进入这个模式后，我想看其他类的代码怎么办？这个时候，就要考验你快捷键的熟练程度了。你可以使用CTRL+E弹出最近使用的文件。又或者使用CTRL+N和CTRL+SHIFT+N定位文件。</p>
</blockquote>
<blockquote>
<p>如何退出这个模式呢？很简单，使用ALT+V弹出view视图，然后选择Exit Presentation Mode 即可。</p>
<p>但是我强烈建议你不要这么做，因为你是可以在Enter Presentation Mode模式下在IDEA里面做任何事情的。当然前提是，你对IDEA足够熟练。</p>
</blockquote>
<h1 id="30-神奇的Inject-language"><a href="#30-神奇的Inject-language" class="headerlink" title="30.神奇的Inject language"></a>30.神奇的Inject language</h1><blockquote>
<p>如果你使用IDEA在编写JSON字符串的时候，然后要一个一个\去转义双引号的话，就实在太不应该了，又烦又容易出错。</p>
<p>在IDEA可以使用Inject language帮我们自动转义双引号。</p>
</blockquote>
<ul>
<li>先将焦点定位到双引号里面，使用alt+enter快捷键弹出inject language视图，并选中Inject language or reference。</li>
</ul>
<p><img src="/myblogs/2020/11/26/idea-hotKey-1/1.jpg"></p>
<ul>
<li>选择后,切记，要直接按下enter回车键，才能弹出inject language列表。在列表中选择 json组件。</li>
</ul>
<p><img src="/myblogs/2020/11/26/idea-hotKey-1/2.jpg"></p>
<ul>
<li>选择完后。鼠标焦点自动会定位在双引号里面，这个时候你再次使用alt+enter就可以看到</li>
</ul>
<p><img src="/myblogs/2020/11/26/idea-hotKey-1/3.jpg"></p>
<ul>
<li>选中Edit JSON Fragment并回车，就可以看到编辑JSON文件的视图了。</li>
</ul>
<p><img src="/myblogs/2020/11/26/idea-hotKey-1/4.jpg" alt="img"></p>
<ul>
<li>可以看到IDEA确实帮我们自动转义双引号了。如果要退出编辑JSON信息的视图，只需要使用ctrl+F4快捷键即可。</li>
<li>Inject language可以支持的语言和操作多到你难以想象，读者可以自行研究。</li>
</ul>
<h1 id="31-强大的symbol"><a href="#31-强大的symbol" class="headerlink" title="31.强大的symbol"></a>31.强大的symbol</h1><blockquote>
<p>如果你依稀记得某个方法名字几个字母，想在IDEA里面找出来，可以怎么做呢？</p>
<p>直接使用ctrl+shift+alt+n，使用symbol来查找即可。</p>
</blockquote>
<p><img src="/myblogs/2020/11/26/idea-hotKey-1/5.jpg" alt="img"></p>
<h1 id="32-idea快捷键和Windows默认快捷键冲突解决（如：Ctrl-Alt-↑或Ctrl-Alt-F12）"><a href="#32-idea快捷键和Windows默认快捷键冲突解决（如：Ctrl-Alt-↑或Ctrl-Alt-F12）" class="headerlink" title="32.idea快捷键和Windows默认快捷键冲突解决（如：Ctrl+Alt+↑或Ctrl+Alt+F12）"></a>32.idea快捷键和Windows默认快捷键冲突解决（如：Ctrl+Alt+↑或Ctrl+Alt+F12）</h1><blockquote>
<p>解决方式：在桌面右键 - 图形选项 - 快捷键 - 禁止 就可以了</p>
</blockquote>
<h1 id="33-格式化代码时，注释被格式化问题"><a href="#33-格式化代码时，注释被格式化问题" class="headerlink" title="33.格式化代码时，注释被格式化问题"></a>33.格式化代码时，注释被格式化问题</h1><p>解决方案</p>
<blockquote>
<p>将enable javadoc formating取消掉</p>
</blockquote>
<p><img src="/myblogs/2020/11/26/idea-hotKey-1/6.jpg" alt="img"></p>
<h1 id="34-import导入报错，更新maven提示Unable-to-import-maven-project-See-logs-for-details"><a href="#34-import导入报错，更新maven提示Unable-to-import-maven-project-See-logs-for-details" class="headerlink" title="34. import导入报错，更新maven提示Unable to import maven project: See logs for details"></a>34. import导入报错，更新maven提示Unable to import maven project: See logs for details</h1><blockquote>
<p>IDEA2019和Maven3.6.2不兼容导致的，需要把Maven降级到3.6.1版本</p>
<p>或者使用idea自带的maven组件</p>
</blockquote>
<p><img src="/myblogs/2020/11/26/idea-hotKey-1/7.jpg" alt="img"></p>
<h1 id="35-IDEA2019版显示出Run-Dashboard"><a href="#35-IDEA2019版显示出Run-Dashboard" class="headerlink" title="35. IDEA2019版显示出Run Dashboard"></a>35. IDEA2019版显示出Run Dashboard</h1><blockquote>
<ol>
<li>启动按钮，点击“Edit Configrations”</li>
<li>左侧，选择Templates</li>
<li>右侧，选择Configurations available in Run Dashboard</li>
<li>点击“+”，选择Spring Boot</li>
</ol>
</blockquote>
<p><img src="/myblogs/2020/11/26/idea-hotKey-1/8.jpg" alt="img"></p>
<h1 id="36-切换大小写"><a href="#36-切换大小写" class="headerlink" title="36.切换大小写"></a>36.切换大小写</h1><blockquote>
<ul>
<li>idea的切换大小写的默认快捷键是ctrl+shift+u</li>
<li>如果默认快捷键冲突，可以双击shift，输入Toggle Case</li>
</ul>
</blockquote>
<h1 id="37-在IDEA中批量删除代码的注释"><a href="#37-在IDEA中批量删除代码的注释" class="headerlink" title="37. 在IDEA中批量删除代码的注释"></a>37. 在IDEA中批量删除代码的注释</h1><blockquote>
<p>先通过IDEA使用Ctrl+R(或者Ctrl+Shift+R()正则表达式替换</p>
<p>(/*([^*]|[\r\n]|(*+([^*/]|[\r\n])))<em>\</em>+/|[ \t]<em>//.</em>)</p>
<p>或者</p>
<p>(/*([^*]|[\r\n]|(*+([^*/]|[\r\n])))<em>\</em>+/)</p>
</blockquote>
<p><img src="/myblogs/2020/11/26/idea-hotKey-1/9.jpg" alt="img"></p>
<blockquote>
<p>被替换以后格式会变乱，可以通过你自己的热键或者IDEA默认格式化（Ctrl+Alt+L）</p>
</blockquote>
<h1 id="38-忽略css、js文件报错"><a href="#38-忽略css、js文件报错" class="headerlink" title="38.忽略css、js文件报错"></a>38.忽略css、js文件报错</h1><blockquote>
<ul>
<li>选择需要处理的文件，按下快捷键：<code>ctrl+alt+shift+h</code></li>
<li>将高亮级别调到<code>None</code>即可</li>
<li>不消失的话关闭重新打开文件，或重启IDEA</li>
</ul>
</blockquote>
<h1 id="39-Terminal替换成Git-Bash"><a href="#39-Terminal替换成Git-Bash" class="headerlink" title="39.Terminal替换成Git Bash"></a>39.Terminal替换成Git Bash</h1><blockquote>
<p>1.IDEA Terminal替换成Git Bash</p>
<p>在IDEA中，打开settings，设置相应的bash路径<br><code>settings</code>–&gt;<code>Tools</code>–&gt;<code>Terminal</code>–&gt;<code>Shell path: C:\Program Files\Git\bin\bash.exe</code></p>
<p>2.解决git commit注释乱码的问题</p>
<p>在<code>C:\Program Files\Git\etc\bash.bashrc</code>末尾行追加如下内容：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">export LANG&#x3D;&quot;zh_CN.UTF-8&quot;</span><br><span class="line">export LC_ALL&#x3D;&quot;zh_CN.UTF-8&quot;</span><br></pre></td></tr></table></figure>

<p>3.重启</p>
<p>重启IDEA或者关闭当前<code>Terminal</code>的<code>session</code>连接，然后<code>New Session</code>连接。</p>
</blockquote>
<p>转载：</p>
<p>参考链接：</p>
<p><a target="_blank" rel="noopener" href="https://www.pianshen.com/article/61151292626/">https://www.pianshen.com/article/61151292626/</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/hanchao5272/article/details/79087934">https://blog.csdn.net/hanchao5272/article/details/79087934</a></p>

          
        
      
    </div>
    
    
    
    
    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://joy_24.gitee.io/myblogs/2020/11/25/rest1/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/myblogs/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="天行健の博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/myblogs/2020/11/25/rest1/" itemprop="url">REST风格框架实战（从MVC到前后端分离）</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-11-25T15:44:32+08:00">
                2020-11-25
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/myblogs/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/" itemprop="url" rel="index">
                    <span itemprop="name">中间件</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  8.3k
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  34
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <p>既然MVC模式这么好，难道它就没有不足的地方吗？我认为MVC至少有以下三点不足：<br>(1)每次请求必须经过“控制器-&gt;模型-&gt;视图”这个流程，用户才能看到最终的展现的界面，这个过程似乎有些复杂；<br>(2)实际上视图是依赖于模型的，换句话说，如果没有模型，视图也无法呈现出最终的效果；<br>(3)渲染视图的过程是在服务端来完成的，最终呈现给浏览器的是带有模型的视图页面，<strong>性能无法得到很好的优化</strong>。</p>
<p>为了使数据展现过程更加直接，并且提供更好的用户体验，我们有必要对MVC模式进行改进。不妨这样来尝试：<br>首先从浏览器发送AJAX请求，然后服务端接受该请求并返回JSON数据返回给浏览器，最后在浏览器中进行界面渲染。改进后的MVC模式如下图所示：</p>
<p><img src="/myblogs/2020/11/25/rest1/0.jpg"></p>
<p>前后端分离的好处：</p>
<p>后端的复用<br>前后端更加自由的独立作业<br>分工造成专业<br>按照标准交付（后端可以做兼职了）</p>
<p>新事物的产生：</p>
<p>前端高速发展<br>JS框架<br>CSS脚本化<br>前端编译技术<br>Single-page Application<br>接口相关技术<br>类似 Swagger 一样的接口标准<br>REST具有更高的可行性<br>数据传输标准 JSend, JSON API<br>HTTP2 对请求的合并<br>后端细化<br>为接口而生的 PHP 框架<br>路由层的显现</p>
<p>没有前后端分离时的开发模式：</p>
<p>后端是跟数据库跟服务器打交道的，前端是跟浏览器打交道的。似乎没有什么问题，大家都这么认为的。当然这没有什么错，我们一直以来都认为仅仅是以浏览器作分界，把这两部分的代码分离出来。但是前后端分离的初衷是为了分离前后端开发人员的职责，同时解决开发模式的问题。但似乎他们的职责在以前甚至于现在都并不明确，虽然前端是跟浏览器打交道，但是最终浏览器拿到的页面是服务器通过模板生成的一个临时静态页面而已。所以，实际上后端也掺和进来了，因为他要处理模板。</p>
<p>当然，一般传统上的开发协作模式有两种：</p>
<p>一种是前端先写一个静态页面，写好后，让后端去套模板。静态页面可以本地开发，也无需考虑业务逻辑只需要实现View即可。不足是还需要后端套模板，这些前端代码后端需要浏览一遍，以免出错。</p>
<p>另一种协作模式是，前端直接去写模板，这样做的问题在于，前端编写过程中很依赖与后端环境，如果当后端没写完的情况下，前端几乎没法干活。</p>
<p>显然这两种方式似乎都有很多问题，但至少这还是目前为止大部分公司所采用的模式。他们从物理层来区分前后端的开发，同时淡化了前端在逻辑上的色彩。<br>由于前端所做的事情就是来实现一个页面的静态版本，所以，大多数公司又给前端工程师们找了点活干。你去看现在公司在招聘的时候前端工程师的要求，除了对页面的基本制作技能外还有额外的设计职责。</p>
<h2 id="一-MVC-概述"><a href="#一-MVC-概述" class="headerlink" title="一. MVC 概述"></a>一. MVC 概述</h2><p><strong>1. 理解 MVC</strong></p>
<p>　　MVC是一种经典的设计模式，全名为Model-View-Controller，即模型-视图-控制器。其中，模型是用于封装数据的载体，例如，在Java中一般通过一个简单的POJO（Plain Ordinary Java Object）来表示，其本质是一个普通的java Bean，包含一系列的成员变量及其getter/setter方法。对于视图而言，它更加偏重于展现，也就是说，视图决定了界面到底长什么样子，在Java中可通过JSP来充当视图，或者通过纯HTML的方式进行展现，而后者才是目前的主流。模型和视图需要通过控制器来进行粘合，例如，用户发送一个HTTP请求，此时该请求首先会进入控制器，然后控制器去获取数据并将其封装为模型，最后将模型传递到视图中进行展现。综上所述，MVC的交互过程如下图所示。</p>
<p><img src="/myblogs/2020/11/25/rest1/1.1.jpg"></p>
<p><strong>2. MVC 模式的优点与不足</strong></p>
<h5 id="MVC模式早在上个世纪70年代就诞生了，直到今天它依然存在，可见生命力相当之强。MVC模式最早应用于Smalltalk语言中，最后在其它许多开发语言中都得到了很好的应用。随着包括Struts、Spring-MVC在内的MVC框架的出现，MVC模式真正落地，并使得开发更加高效、代码耦合度尽量减小、应用程序各部分的职责更加清晰。"><a href="#MVC模式早在上个世纪70年代就诞生了，直到今天它依然存在，可见生命力相当之强。MVC模式最早应用于Smalltalk语言中，最后在其它许多开发语言中都得到了很好的应用。随着包括Struts、Spring-MVC在内的MVC框架的出现，MVC模式真正落地，并使得开发更加高效、代码耦合度尽量减小、应用程序各部分的职责更加清晰。" class="headerlink" title="　　MVC模式早在上个世纪70年代就诞生了，直到今天它依然存在，可见生命力相当之强。MVC模式最早应用于Smalltalk语言中，最后在其它许多开发语言中都得到了很好的应用。随着包括Struts、Spring MVC在内的MVC框架的出现，MVC模式真正落地，并使得开发更加高效、代码耦合度尽量减小、应用程序各部分的职责更加清晰。"></a>　　MVC模式早在上个世纪70年代就诞生了，直到今天它依然存在，可见生命力相当之强。MVC模式最早应用于Smalltalk语言中，最后在其它许多开发语言中都得到了很好的应用。随着包括Struts、Spring MVC在内的MVC框架的出现，MVC模式真正落地，并使得开发更加高效、代码耦合度尽量减小、应用程序各部分的职责更加清晰。</h5><p>　　既然MVC模式这么好，难道它就没有不足的地方吗？我认为MVC至少有以下三点不足：</p>
<ul>
<li><p>每次请求必须经过“控制器-&gt;模型-&gt;视图”这个流程，用户才能看到最终的展现的界面，这个过程似乎有些复杂；</p>
</li>
<li><p>实际上视图是依赖于模型的，换句话说，如果没有模型，视图也无法呈现出最终的效果；</p>
</li>
<li><p>渲染视图的过程是在服务端来完成的，最终呈现给浏览器的是带有模型的视图页面，性能无法得到很好的优化。</p>
<p>　　为了使数据展现过程更加直接，并且提供更好的用户体验，我们有必要对MVC模式进行改进。不妨这样来尝试：<strong>首先从浏览器发送AJAX请求，然后服务端接受该请求并返回JSON数据返回给浏览器，最后在浏览器中进行界面渲染。</strong>改进后的MVC模式如下图所示：</p>
</li>
</ul>
<p><img src="/myblogs/2020/11/25/rest1/1.2.1.jpg"></p>
<p>也就是说，我们输入的是AJAX请求，输出的是JSON数据，市面上有这样的技术来实现这个功能吗？答案是REST。</p>
<p>　　REST全称是Representational State Transfer（表述性状态转移），它是Roy Fielding博士在2000年写的一篇关于软件架构风格的论文，此文一出，威震四方！国内外许多知名互联网公司纷纷开始采用这种轻量级的Web服务，大家习惯将其称为RESTful Web Services，或简称REST服务。]</p>
<p>　　如果将浏览器这一端视为前端，而服务器那一端视为后端的话，可以将以上改进后的MVC模式简化为以下前后端分离模式，如下图所示：</p>
<p><img src="/myblogs/2020/11/25/rest1/1.2.2.jpg"></p>
<p>可见，<strong>采用REST分格的架构可以使得前端关注界面展现，后端关注业务逻辑，分工明确，职责清晰。</strong>那么，如何使用REST架构将应用程序进行前后端分离呢？我们接下来继续探讨，首先我们需要认识REST。</p>
<h2 id="二-认识-REST"><a href="#二-认识-REST" class="headerlink" title="二. 认识 REST"></a>二. 认识 REST</h2><p>　　REST本质上是使用URL来访问资源的一种方式。众所周知，URL(Uniform Resoure Locator:统一资源定位器)就是我们平常使用的请求地址了，其中包括两部分：请求方式与请求路径，比较常见的请求方式是GET与POST，但在REST中又提出了几种其它类型的请求方式，汇总起来有六种：GET、POST、PUT、DELETE、HEAD、OPTIONS。尤其是前四种，正好与CRUD（Create-Retrieve-Update-Delete，增删改查）四种操作相对应，例如，GET（查）、POST（增）、PUT（改）、DELETE（删），这正是REST与CRUD的异曲同工之妙！<strong>需要强调的是，REST是面向资源(ROA)的，这里提到的资源，实际上就是我们常说的领域对象，在系统设计过程中，我们经常通过领域对象来进行数据建模。</strong></p>
<p>　　REST是一个无状态的架构模式，因为在任何时候都可以由客户端发出请求到服务端，最终返回自己想要的数据，当前请求不会受到上次请求的影响。也就是说，<strong>服务端将内部资源发布REST服务，客户端通过URL来定位这些资源并通过HTTP协议来访问它们。</strong>下面我们举几个例子对REST请求进行简单描述：</p>
<p><img src="/myblogs/2020/11/25/rest1/2.jpg"></p>
<p>可见，请求路径相同但请求方式不同，所代表的业务操作也不同，例如，/advertiser/1这个请求，带有GET、PUT、DELETE三种不同的请求方式，对应三种不同的业务操作。</p>
<p>　　虽然REST看起来还是很简单的，实际上我们往往需要提供一个REST框架，让其实现前后端分离架构，让开发人员将精力集中在业务上，而并非那些具体的技术细节。下面我们将使用Java技术来实现这个REST框架，整体框架会基于Spring进行开发。</p>
<h2 id="三-实现REST框架"><a href="#三-实现REST框架" class="headerlink" title="三. 实现REST框架"></a>三. 实现REST框架</h2><p><strong>1、统一响应结构</strong></p>
<p>　　使用REST框架实现前后端分离架构，我们需要首先确定返回的JSON响应结构是统一的，也就是说，每个REST请求将返回相同结构的JSON响应结构。不妨定义一个相对通用的JSON响应结构，其中包含两部分：元数据与返回值，其中，元数据表示操作是否成功与返回值消息等，返回值对应服务端方法所返回的数据。该JSON响应结构如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">        &quot;meta&quot;: &#123;</span><br><span class="line">            &quot;success&quot;: true,</span><br><span class="line">            &quot;message&quot;: &quot;ok&quot;</span><br><span class="line">        &#125;,</span><br><span class="line">        &quot;data&quot;: ...</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>为了在框架中映射以上JSON响应结构，我们需要编写一个Response类与其对应：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;**</span><br><span class="line"> * Title: 统一响应结构 </span><br><span class="line"> * Description:使用REST框架实现前后端分离架构，我们需要首先确定返回的JSON响应结构是统一的，</span><br><span class="line"> * 也就是说，每个REST请求将返回相同结构的JSON响应结构。不妨定义一个相对通用的JSON响应结构，其</span><br><span class="line"> * 中包含两部分：元数据与返回值，其中，元数据表示操作是否成功与返回值消息等，返回值对应服务端方法所返回的数据。</span><br><span class="line"> * &#123; &quot;meta&quot;: &#123; &quot;success&quot;: true, &quot;message&quot;: &quot;ok&quot; &#125;, &quot;data&quot;: ... &#125;</span><br><span class="line"> * </span><br><span class="line"> * @author rico</span><br><span class="line"> * @created 2017年7月4日 下午5:06:00</span><br><span class="line"> *&#x2F;</span><br><span class="line">public class Response &#123;</span><br><span class="line"></span><br><span class="line">    private static final String OK &#x3D; &quot;ok&quot;;</span><br><span class="line">    private static final String ERROR &#x3D; &quot;error&quot;;</span><br><span class="line"></span><br><span class="line">    private Meta meta;     &#x2F;&#x2F; 元数据</span><br><span class="line">    private Object data;   &#x2F;&#x2F; 响应内容</span><br><span class="line"></span><br><span class="line">    public Response success() &#123;</span><br><span class="line">        this.meta &#x3D; new Meta(true, OK);</span><br><span class="line">        return this;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public Response success(Object data) &#123;</span><br><span class="line">        this.meta &#x3D; new Meta(true, OK);</span><br><span class="line">        this.data &#x3D; data;</span><br><span class="line">        return this;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public Response failure() &#123;</span><br><span class="line">        this.meta &#x3D; new Meta(false, ERROR);</span><br><span class="line">        return this;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public Response failure(String message) &#123;</span><br><span class="line">        this.meta &#x3D; new Meta(false, message);</span><br><span class="line">        return this;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public Meta getMeta() &#123;</span><br><span class="line">        return meta;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public Object getData() &#123;</span><br><span class="line">        return data;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    &#x2F;**</span><br><span class="line">     * Title: 请求元数据</span><br><span class="line">     * @author rico</span><br><span class="line">     * @created 2017年7月4日 下午5:08:12</span><br><span class="line">     *&#x2F;</span><br><span class="line">    public class Meta &#123;</span><br><span class="line"></span><br><span class="line">        private boolean success;</span><br><span class="line">        private String message;</span><br><span class="line"></span><br><span class="line">        public Meta(boolean success) &#123;</span><br><span class="line">            this.success &#x3D; success;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        public Meta(boolean success, String message) &#123;</span><br><span class="line">            this.success &#x3D; success;</span><br><span class="line">            this.message &#x3D; message;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        public boolean isSuccess() &#123;</span><br><span class="line">            return success;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        public String getMessage() &#123;</span><br><span class="line">            return message;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>以上Response类包括两类通用返回值消息：ok 与 error，还包括两个常用的操作方法：success( )与failure( )，通过一个内部类来展现元数据结构，我们在下文中多次会使用该Response类。</p>
<p>实现该REST框架需要考虑许多问题，首当其冲的就是前后数据流转问题，即HTTP消息与Java对象之间的转化问题。</p>
<p><strong>2、前后台数据流转</strong><br>　　<br>　　前后台数据流转问题具体指的是什么？不妨通过一些例子进行说明。比如，通过浏览器发送了一个普通的HTTP请求，该请求携带了一个JSON格式的参数，在服务端需要将该JSON参数转换为普通的Java对象；再比如，在服务端获取了数据，此时该数据是一个普通的Java对象，然后需要将这个Java对象转换为JSON字符串，并将其返回到浏览器中进行渲染，这个过程就涉及HTTP消息与Java对象之间的转化问题。</p>
<p>　　实际上，Spring MVC已经为我们提供了这类转化特性，只需在Controller的方法参数中使用@RequestBody注解定义需要转化的参数即可；<br>类似地，若需要对Controller的方法返回值进行转化，则需要在该返回值上使用@ResponseBody注解来定义，如以下代码片段：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">@RequestMapping(&quot;&#x2F;users&quot;)</span><br><span class="line">public class UserController &#123;</span><br><span class="line"></span><br><span class="line">    private UserService userService;</span><br><span class="line"></span><br><span class="line">    &#x2F;** Log4j日志处理(@author: rico) *&#x2F;</span><br><span class="line">    private static final Logger log &#x3D; Logger.getLogger(UserController.class);</span><br><span class="line"></span><br><span class="line">    public UserService getUserService() &#123;</span><br><span class="line">        return userService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Resource(name &#x3D; &quot;userService&quot;)</span><br><span class="line">    public void setUserService(UserService userService) &#123;</span><br><span class="line">        this.userService &#x3D; userService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @RequestMapping(value &#x3D; &quot;&#x2F;user&quot;, method &#x3D; RequestMethod.PUT, produces &#x3D; &quot;application&#x2F;json&quot;, </span><br><span class="line">            consumes &#x3D; &quot;application&#x2F;json&quot;)</span><br><span class="line">    @ResponseBody     &#x2F;&#x2F; 将 Java 对象转化为特定的HTTP消息</span><br><span class="line">    public User addUser(@RequestBody User user) &#123;  &#x2F;&#x2F; 将接收到的HTTP消息转化为Java对象</span><br><span class="line">        userService.addUser(user);</span><br><span class="line">        log.debug(&quot;添加用户 :&quot; + user);</span><br><span class="line">        return user;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>当然，@ResponseBody注解也可以定义在类上，这样所有的方法都继承了该特性。由于经常会使用到@ResponseBody注解，所以Spring提供了一个名为@RestController的注解来取代以上的@Controller注解，这样我们就可以省略返回值前面的@ResponseBody注解了，但参数前面的@RequestBody注解是无法省略的。实际上，看看Spring中对应@RestController注解的源码便可知晓：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">@Target(&#123;ElementType.TYPE&#125;)  </span><br><span class="line">@Retention(RetentionPolicy.RUNTIME)  </span><br><span class="line">@Documented  </span><br><span class="line">@Controller  </span><br><span class="line">@ResponseBody  </span><br><span class="line">public @interface RestController &#123;  </span><br><span class="line"></span><br><span class="line">    String value() default &quot;&quot;;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可见，@RestController注解已经被@Controller与@ResponseBody注解定义过了，Spring框架会识别这类注解。需要注意的是，该特性在Spring 4.0中才引入。<br>因此，我们可将以上代码进行如下改写：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">@RestController</span><br><span class="line">@RequestMapping(&quot;&#x2F;users&quot;)</span><br><span class="line">public class UserController &#123;</span><br><span class="line"></span><br><span class="line">    private UserService userService;</span><br><span class="line"></span><br><span class="line">    &#x2F;** Log4j日志处理(@author: rico) *&#x2F;</span><br><span class="line">    private static final Logger log &#x3D; Logger.getLogger(UserController.class);</span><br><span class="line"></span><br><span class="line">    public UserService getUserService() &#123;</span><br><span class="line">        return userService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Resource(name &#x3D; &quot;userService&quot;)</span><br><span class="line">    public void setUserService(UserService userService) &#123;</span><br><span class="line">        this.userService &#x3D; userService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @RequestMapping(value &#x3D; &quot;&#x2F;user&quot;, method &#x3D; RequestMethod.PUT, produces &#x3D; &quot;application&#x2F;json&quot;, </span><br><span class="line">            consumes &#x3D; &quot;application&#x2F;json&quot;)</span><br><span class="line">    public User addUser(@RequestBody User user) &#123;  &#x2F;&#x2F; 将接收到的HTTP消息转化为Java对象</span><br><span class="line">        userService.addUser(user);</span><br><span class="line">        log.debug(&quot;添加用户 :&quot; + user);</span><br><span class="line">        return user;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>除了使用注解来定义消息转化行为以外，我们还需要添加Jackson包进行支持，Maven依赖如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- JSON: jackson --&gt;</span><br><span class="line">&lt;dependency&gt;</span><br><span class="line">    &lt;groupId&gt;org.codehaus.jackson&lt;&#x2F;groupId&gt;</span><br><span class="line">    &lt;artifactId&gt;jackson-core-asl&lt;&#x2F;artifactId&gt;</span><br><span class="line">    &lt;version&gt;1.9.12&lt;&#x2F;version&gt;</span><br><span class="line">&lt;&#x2F;dependency&gt;</span><br><span class="line">&lt;dependency&gt;</span><br><span class="line">    &lt;groupId&gt;org.codehaus.jackson&lt;&#x2F;groupId&gt;</span><br><span class="line">    &lt;artifactId&gt;jackson-mapper-lgpl&lt;&#x2F;artifactId&gt;</span><br><span class="line">    &lt;version&gt;1.9.12&lt;&#x2F;version&gt;</span><br><span class="line">&lt;&#x2F;dependency&gt;</span><br></pre></td></tr></table></figure>

<p>在Spring配置文件中添加以下配置即可：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 该配置会自动注册RequestMappingHandlerMapping与RequestMappingHandlerAdapter两个Bean，</span><br><span class="line">    这是SpringMVC为@Controllers分发请求所必需的，并提供了数据绑定支持、@NumberFormatannotation支持、</span><br><span class="line">    @DateTimeFormat支持、@Valid支持、读写XML的支持和读写JSON的支持等功能。 --&gt;</span><br><span class="line">    &lt;mvc:annotation-driven &#x2F;&gt;</span><br></pre></td></tr></table></figure>

<p>通过以上过程，我们已经完成了一个基于SpringMVC的REST框架，只不过该框架还非常单薄，还缺乏很多关键性特性，尤其是异常处理。</p>
<p><strong>3、处理异常行为</strong></p>
<p>　　在Spring MVC中，我们可以使用AOP技术，编写一个全局的异常处理切面类，用它来统一处理所有的异常行为，在Spring 3.2中才开始提供。使用很简单，只需定义一个类，并通过@ControllerAdvice注解将其标注即可，同时需要使用@ResponseBody注解表示返回值可序列化为JSON字符串。<br>代码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;**        </span><br><span class="line"> * Title: 全局异常处理切面    </span><br><span class="line"> * Description: 利用 @ControllerAdvice + @ExceptionHandler 组合处理Controller层RuntimeException异常</span><br><span class="line"> * @author rico       </span><br><span class="line"> * @created 2017年7月4日 下午4:29:07    </span><br><span class="line"> *&#x2F;      </span><br><span class="line">@ControllerAdvice   &#x2F;&#x2F; 控制器增强</span><br><span class="line">@ResponseBody</span><br><span class="line">public class ExceptionAspect &#123;</span><br><span class="line"></span><br><span class="line">    &#x2F;** Log4j日志处理(@author: rico) *&#x2F;</span><br><span class="line">    private static final Logger log &#x3D; Logger.getLogger(ExceptionAspect.class);</span><br><span class="line"></span><br><span class="line">    &#x2F;**</span><br><span class="line">     * 400 - Bad Request</span><br><span class="line">     *&#x2F;</span><br><span class="line">    @ResponseStatus(HttpStatus.BAD_REQUEST)</span><br><span class="line">    @ExceptionHandler(HttpMessageNotReadableException.class)</span><br><span class="line">    public Response handleHttpMessageNotReadableException(</span><br><span class="line">            HttpMessageNotReadableException e) &#123;</span><br><span class="line">        log.error(&quot;could_not_read_json...&quot;, e);</span><br><span class="line">        return new Response().failure(&quot;could_not_read_json&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    &#x2F;**</span><br><span class="line">     * 400 - Bad Request</span><br><span class="line">     *&#x2F;</span><br><span class="line">    @ResponseStatus(HttpStatus.BAD_REQUEST)</span><br><span class="line">    @ExceptionHandler(&#123;MethodArgumentNotValidException.class&#125;)</span><br><span class="line">    public Response handleValidationException(MethodArgumentNotValidException e) &#123;</span><br><span class="line">        log.error(&quot;parameter_validation_exception...&quot;, e);</span><br><span class="line">        return new Response().failure(&quot;parameter_validation_exception&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    &#x2F;**</span><br><span class="line">     * 405 - Method Not Allowed。HttpRequestMethodNotSupportedException</span><br><span class="line">     * 是ServletException的子类,需要Servlet API支持</span><br><span class="line">     *&#x2F;</span><br><span class="line">    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)</span><br><span class="line">    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)</span><br><span class="line">    public Response handleHttpRequestMethodNotSupportedException(</span><br><span class="line">            HttpRequestMethodNotSupportedException e) &#123;</span><br><span class="line">        log.error(&quot;request_method_not_supported...&quot;, e);</span><br><span class="line">        return new Response().failure(&quot;request_method_not_supported&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    &#x2F;**</span><br><span class="line">     * 415 - Unsupported Media Type。HttpMediaTypeNotSupportedException</span><br><span class="line">     * 是ServletException的子类,需要Servlet API支持</span><br><span class="line">     *&#x2F;</span><br><span class="line">    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)</span><br><span class="line">    @ExceptionHandler(&#123; HttpMediaTypeNotSupportedException.class &#125;)</span><br><span class="line">    public Response handleHttpMediaTypeNotSupportedException(Exception e) &#123;</span><br><span class="line">        log.error(&quot;content_type_not_supported...&quot;, e);</span><br><span class="line">        return new Response().failure(&quot;content_type_not_supported&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    &#x2F;**</span><br><span class="line">     * 500 - Internal Server Error</span><br><span class="line">     *&#x2F;</span><br><span class="line">    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)</span><br><span class="line">    @ExceptionHandler(TokenException.class)</span><br><span class="line">    public Response handleTokenException(Exception e) &#123;</span><br><span class="line">        log.error(&quot;Token is invaild...&quot;, e);</span><br><span class="line">        return new Response().failure(&quot;Token is invaild&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    &#x2F;**</span><br><span class="line">     * 500 - Internal Server Error</span><br><span class="line">     *&#x2F;</span><br><span class="line">    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)</span><br><span class="line">    @ExceptionHandler(Exception.class)</span><br><span class="line">    public Response handleException(Exception e) &#123;</span><br><span class="line">        log.error(&quot;Internal Server Error...&quot;, e);</span><br><span class="line">        return new Response().failure(&quot;Internal Server Error&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可见，在ExceptionAdvice类中包含一系列的异常处理方法，每个方法都通过@ResponseStatus注解定义了响应状态码，此外还通过@ExceptionHandler注解指定了具体需要拦截的异常类。以上过程只是包含了一部分的异常情况，若需处理其它异常，可添加方法具体的方法。需要注意的是，在运行时从上往下依次调用每个异常处理方法，匹配当前异常类型是否与@ExceptionHandler注解所定义的异常相匹配，若匹配，则执行该方法，同时忽略后续所有的异常处理方法，最终会返回经JSON序列化后的Response对象。</p>
<p><strong>4、支持参数验证</strong></p>
<p>　　我们回到上文所提到的示例，这里处理一个普通的PUT请求，代码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">@RestController</span><br><span class="line">@RequestMapping(&quot;&#x2F;users&quot;)</span><br><span class="line">public class UserController &#123;</span><br><span class="line"></span><br><span class="line">    private UserService userService;</span><br><span class="line"></span><br><span class="line">    &#x2F;** Log4j日志处理(@author: rico) *&#x2F;</span><br><span class="line">    private static final Logger log &#x3D; Logger.getLogger(UserController.class);</span><br><span class="line"></span><br><span class="line">    public UserService getUserService() &#123;</span><br><span class="line">        return userService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Resource(name &#x3D; &quot;userService&quot;)</span><br><span class="line">    public void setUserService(UserService userService) &#123;</span><br><span class="line">        this.userService &#x3D; userService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @RequestMapping(value &#x3D; &quot;&#x2F;user&quot;, method &#x3D; RequestMethod.PUT, produces &#x3D; &quot;application&#x2F;json&quot;, </span><br><span class="line">            consumes &#x3D; &quot;application&#x2F;json&quot;)</span><br><span class="line">    public User addUser(@RequestBody User user) &#123;  &#x2F;&#x2F; 将接收到的HTTP消息转化为Java对象</span><br><span class="line">        userService.addUser(user);</span><br><span class="line">        log.debug(&quot;添加用户 :&quot; + user);</span><br><span class="line">        return user;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>其中，User参数包含若干属性，通过以下类结构可见，它是一个传统的POJO：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">public class User implements Serializable&#123;</span><br><span class="line"></span><br><span class="line">    private static final long serialVersionUID &#x3D; 1L;</span><br><span class="line"></span><br><span class="line">    private int id;</span><br><span class="line">    private String uname;</span><br><span class="line">    private String passwd;</span><br><span class="line">    private String gentle;</span><br><span class="line">    private String email;</span><br><span class="line">    private String city;</span><br><span class="line"></span><br><span class="line">    public User() &#123;</span><br><span class="line">        super();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    &#x2F;&#x2F; getter&#x2F;setter</span><br><span class="line"></span><br><span class="line">    &#x2F;&#x2F; toString</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果业务上需要确保User对象的uname属性必填，如何实现呢？若将这类参数验证的代码写死在Controller中，势必会与正常的业务逻辑搅在一起，导致责任不够单一，违背于“单一责任原则”。建议将其参数验证行为从Controller中剥离出来，放到另外的类中，这里仅通过@Valid注解来定义uname参数，并通过Bean Validation的参考实现Hibernate Validator的@NotEmpty注解来定义User类中的uname属性，就像下面这样：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">@RestController</span><br><span class="line">@RequestMapping(&quot;&#x2F;users&quot;)</span><br><span class="line">public class UserController &#123;</span><br><span class="line"></span><br><span class="line">    private UserService userService;</span><br><span class="line"></span><br><span class="line">    &#x2F;** Log4j日志处理(@author: rico) *&#x2F;</span><br><span class="line">    private static final Logger log &#x3D; Logger.getLogger(UserController.class);</span><br><span class="line"></span><br><span class="line">    public UserService getUserService() &#123;</span><br><span class="line">        return userService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Resource(name &#x3D; &quot;userService&quot;)</span><br><span class="line">    public void setUserService(UserService userService) &#123;</span><br><span class="line">        this.userService &#x3D; userService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @RequestMapping(value &#x3D; &quot;&#x2F;user&quot;, method &#x3D; RequestMethod.PUT, produces &#x3D; &quot;application&#x2F;json&quot;, </span><br><span class="line">            consumes &#x3D; &quot;application&#x2F;json&quot;)</span><br><span class="line">    public User addUser(@RequestBody  @Valid User user) &#123;  &#x2F;&#x2F; 将接收到的HTTP消息转化为Java对象</span><br><span class="line">        userService.addUser(user);</span><br><span class="line">        log.debug(&quot;添加用户 :&quot; + user);</span><br><span class="line">        return user;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">public class User implements Serializable&#123;</span><br><span class="line"></span><br><span class="line">    private static final long serialVersionUID &#x3D; 1L;</span><br><span class="line"></span><br><span class="line">    private int id;</span><br><span class="line">    @NotEmpty</span><br><span class="line">    private String uname;</span><br><span class="line">    private String passwd;</span><br><span class="line">    private String gentle;</span><br><span class="line">    private String email;</span><br><span class="line">    private String city;</span><br><span class="line"></span><br><span class="line">    public User() &#123;</span><br><span class="line">        super();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    &#x2F;&#x2F; getter&#x2F;setter</span><br><span class="line"></span><br><span class="line">    &#x2F;&#x2F; toString</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里的@Valid注解实际上是Validation Bean规范提供的注解，该规范已由Hibernate Validator框架实现，因此需要添加以下Maven依赖到pom.xml文件中：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;dependency&gt;  </span><br><span class="line">    &lt;groupId&gt;org.hibernate&lt;&#x2F;groupId&gt;  </span><br><span class="line">    &lt;artifactId&gt;hibernate-validator&lt;&#x2F;artifactId&gt;  </span><br><span class="line">    &lt;version&gt;$&#123;hibernate-validator.version&#125;&lt;&#x2F;version&gt;  </span><br><span class="line">&lt;&#x2F;dependency&gt;  </span><br></pre></td></tr></table></figure>

<p>需要注意的是，Hibernate Validator与Hibernate没有任何依赖关系，唯一有联系的只是都属于JBoss公司的开源项目而已。然后，我们需要在Spring配置文件中开启该特性，需添加如下配置：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;bean class&#x3D;&quot;org.springframework.validation.beanvalidation.MethodValidationPostProcessor&quot;&#x2F;&gt; </span><br></pre></td></tr></table></figure>

<p>最后，我们在全局异常处理类中添加对参数验证异常的处理方法，代码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">@ControllerAdvice  </span><br><span class="line">@ResponseBody  </span><br><span class="line">public class ExceptionAdvice &#123;  </span><br><span class="line"></span><br><span class="line">    &#x2F;** </span><br><span class="line">     * 400 - Bad Request </span><br><span class="line">     *&#x2F;  </span><br><span class="line">    @ResponseStatus(HttpStatus.BAD_REQUEST)  </span><br><span class="line">    @ExceptionHandler(ValidationException.class)  </span><br><span class="line">    public Response handleValidationException(ValidationException e) &#123;  </span><br><span class="line">        logger.error(&quot;参数验证失败&quot;, e);  </span><br><span class="line">        return new Response().failure(&quot;validation_exception&quot;);  </span><br><span class="line">    &#125;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>至此，REST框架已集成了Bean Validation特性，我们可以使用各种注解来完成所需的参数验证行为了。 </p>
<p>看似该框架可以在本地成功跑起来，整个架构包含两个应用，前端应用提供纯静态的HTML页面，后端应用发布REST API，前端需要通过AJAX调用后端发布的REST API，然而AJAX是不支持跨域访问的，也就是说，前后端两个应用必须在同一个域名下才能访问。这是非常严重的技术障碍，一定需要找到解决方案。</p>
<hr>
<p><strong>5、解决跨域问题</strong></p>
<p>　　比如，前端应用为静态站点且部署在<a target="_blank" rel="noopener" href="http://web.xxx.com/">http://web.xxx.com</a>域下，后端应用发布REST API并部署在<a target="_blank" rel="noopener" href="http://api.xxx.com/">http://api.xxx.com</a>域下，如何使前端应用通过AJAX跨域访问后端应用呢？这需要使用到CORS技术来实现，这也是目前最好的解决方案了。</p>
<p>　　CORS全称为Cross Origin Resource Sharing（跨域资源共享），服务端只需添加相关响应头信息，即可实现客户端发出AJAX跨域请求。</p>
<p>　　CORS技术非常简单，易于实现，目前绝大多数浏览器均已支持该技术（IE8浏览器也支持了），服务端可通过任何编程语言来实现，只要能将CORS响应头写入response对象中即可。</p>
<p>　　下面我们继续扩展REST框架，通过CORS技术实现AJAX跨域访问。首先，我们需要编写一个Filter，用于过滤所有的HTTP请求，并将CORS响应头写入response对象中，代码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;**        </span><br><span class="line"> * Title: 跨域访问处理(跨域资源共享)    </span><br><span class="line"> * Description: 解决前后端分离架构中的跨域问题</span><br><span class="line"> * @author rico       </span><br><span class="line"> * @created 2017年7月4日 下午5:00:09    </span><br><span class="line"> *&#x2F;      </span><br><span class="line">public class CorsFilter implements Filter &#123;</span><br><span class="line"></span><br><span class="line">    &#x2F;** Log4j日志处理(@author: rico) *&#x2F;</span><br><span class="line">    private static final Logger log &#x3D; Logger.getLogger(UserController.class);</span><br><span class="line"></span><br><span class="line">    private String allowOrigin;</span><br><span class="line">    private String allowMethods;</span><br><span class="line">    private String allowCredentials;</span><br><span class="line">    private String allowHeaders;</span><br><span class="line">    private String exposeHeaders;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void init(FilterConfig filterConfig) throws ServletException &#123;</span><br><span class="line">        allowOrigin &#x3D; filterConfig.getInitParameter(&quot;allowOrigin&quot;);</span><br><span class="line">        allowMethods &#x3D; filterConfig.getInitParameter(&quot;allowMethods&quot;);</span><br><span class="line">        allowCredentials &#x3D; filterConfig.getInitParameter(&quot;allowCredentials&quot;);</span><br><span class="line">        allowHeaders &#x3D; filterConfig.getInitParameter(&quot;allowHeaders&quot;);</span><br><span class="line">        exposeHeaders &#x3D; filterConfig.getInitParameter(&quot;exposeHeaders&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &#x2F;** </span><br><span class="line">     * @description 通过CORS技术实现AJAX跨域访问,只要将CORS响应头写入response对象中即可</span><br><span class="line">     * @author rico       </span><br><span class="line">     * @created 2017年7月4日 下午5:02:38      </span><br><span class="line">     * @param req</span><br><span class="line">     * @param res</span><br><span class="line">     * @param chain</span><br><span class="line">     * @throws IOException</span><br><span class="line">     * @throws ServletException     </span><br><span class="line">     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)     </span><br><span class="line">     *&#x2F;  </span><br><span class="line">    @Override</span><br><span class="line">    public void doFilter(ServletRequest req, ServletResponse res,</span><br><span class="line">            FilterChain chain) throws IOException, ServletException &#123;</span><br><span class="line">        HttpServletRequest request &#x3D; (HttpServletRequest) req;</span><br><span class="line">        HttpServletResponse response &#x3D; (HttpServletResponse) res;</span><br><span class="line">        String currentOrigin &#x3D; request.getHeader(&quot;Origin&quot;);</span><br><span class="line">        log.debug(&quot;currentOrigin : &quot; + currentOrigin);</span><br><span class="line">        if (StringUtil.isNotEmpty(allowOrigin)) &#123;</span><br><span class="line">            List&lt;String&gt; allowOriginList &#x3D; Arrays</span><br><span class="line">                    .asList(allowOrigin.split(&quot;,&quot;));</span><br><span class="line">            log.debug(&quot;allowOriginList : &quot; + allowOrigin);</span><br><span class="line">            if (CollectionUtil.isNotEmpty(allowOriginList)) &#123;</span><br><span class="line">                if (allowOriginList.contains(currentOrigin)) &#123;</span><br><span class="line">                    response.setHeader(&quot;Access-Control-Allow-Origin&quot;,</span><br><span class="line">                            currentOrigin);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        if (StringUtil.isNotEmpty(allowMethods)) &#123;</span><br><span class="line">            response.setHeader(&quot;Access-Control-Allow-Methods&quot;, allowMethods);</span><br><span class="line">        &#125;</span><br><span class="line">        if (StringUtil.isNotEmpty(allowCredentials)) &#123;</span><br><span class="line">            response.setHeader(&quot;Access-Control-Allow-Credentials&quot;,</span><br><span class="line">                    allowCredentials);</span><br><span class="line">        &#125;</span><br><span class="line">        if (StringUtil.isNotEmpty(allowHeaders)) &#123;</span><br><span class="line">            response.setHeader(&quot;Access-Control-Allow-Headers&quot;, allowHeaders);</span><br><span class="line">        &#125;</span><br><span class="line">        if (StringUtil.isNotEmpty(exposeHeaders)) &#123;</span><br><span class="line">            response.setHeader(&quot;Access-Control-Expose-Headers&quot;, exposeHeaders);</span><br><span class="line">        &#125;</span><br><span class="line">        chain.doFilter(req, res);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void destroy() &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>以上CorsFilter将从web.xml中读取相关Filter初始化参数，并将在处理HTTP请求时将这些参数写入对应的CORS响应头中，下面大致描述一下这些CORS响应头的意义：</p>
<ul>
<li><p>Access-Control-Allow-Origin：允许访问的客户端域名，例如：<a target="_blank" rel="noopener" href="http://web.xxx.com/">http://web.xxx.com</a>，若为*，则表示从任意域都能访问，即不做任何限制；</p>
</li>
<li><p>Access-Control-Allow-Methods：允许访问的方法名，多个方法名用逗号分割，例如：GET,POST,PUT,DELETE,OPTIONS；</p>
</li>
<li><p>Access-Control-Allow-Credentials：是否允许请求带有验证信息，若要获取客户端域下的cookie时，需要将其设置为true；</p>
</li>
<li><p>Access-Control-Allow-Headers：允许服务端访问的客户端请求头，多个请求头用逗号分割，例如：Content-Type；</p>
</li>
<li><p>Access-Control-Expose-Headers：允许客户端访问的服务端响应头，多个响应头用逗号分割。</p>
<p>　需要注意的是，CORS规范中定义Access-Control-Allow-Origin只允许两种取值，要么为*，要么为具体的域名，也就是说，不支持同时配置多个域名。为了解决跨多个域的问题，需要在代码中做一些处理，这里将Filter初始化参数作为一个域名的集合（用逗号分隔），只需从当前请求中获取Origin请求头，就知道是从哪个域中发出的请求，若该请求在以上允许的域名集合中，则将其放入Access-Control-Allow-Origin响应头，这样跨多个域的问题就轻松解决了。以下是web.xml中配置CorsFilter的方法：</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 通过CORS技术实现AJAX跨域访问 --&gt;</span><br><span class="line">    &lt;filter&gt;</span><br><span class="line">        &lt;filter-name&gt;corsFilter&lt;&#x2F;filter-name&gt;</span><br><span class="line">        &lt;filter-class&gt;cn.edu.tju.rico.filter.CorsFilter&lt;&#x2F;filter-class&gt;</span><br><span class="line">        &lt;init-param&gt;</span><br><span class="line">            &lt;param-name&gt;allowOrigin&lt;&#x2F;param-name&gt;</span><br><span class="line">            &lt;param-value&gt;http:&#x2F;&#x2F;localhost:8020&lt;&#x2F;param-value&gt;</span><br><span class="line">        &lt;&#x2F;init-param&gt;</span><br><span class="line">        &lt;init-param&gt;</span><br><span class="line">            &lt;param-name&gt;allowMethods&lt;&#x2F;param-name&gt;</span><br><span class="line">            &lt;param-value&gt;GET,POST,PUT,DELETE,OPTIONS&lt;&#x2F;param-value&gt;</span><br><span class="line">        &lt;&#x2F;init-param&gt;</span><br><span class="line">        &lt;init-param&gt;</span><br><span class="line">            &lt;param-name&gt;allowCredentials&lt;&#x2F;param-name&gt;</span><br><span class="line">            &lt;param-value&gt;true&lt;&#x2F;param-value&gt;</span><br><span class="line">        &lt;&#x2F;init-param&gt;</span><br><span class="line">        &lt;init-param&gt;</span><br><span class="line">            &lt;param-name&gt;allowHeaders&lt;&#x2F;param-name&gt;</span><br><span class="line">            &lt;param-value&gt;Content-Type,X-Token&lt;&#x2F;param-value&gt;</span><br><span class="line">        &lt;&#x2F;init-param&gt;</span><br><span class="line">    &lt;&#x2F;filter&gt;</span><br><span class="line">    &lt;filter-mapping&gt;</span><br><span class="line">        &lt;filter-name&gt;corsFilter&lt;&#x2F;filter-name&gt;</span><br><span class="line">        &lt;url-pattern&gt;&#x2F;*&lt;&#x2F;url-pattern&gt;</span><br><span class="line">    &lt;&#x2F;filter-mapping&gt;</span><br></pre></td></tr></table></figure>

<p>完成以上过程即可实现AJAX跨域功能了，但似乎还存在另外一个问题，由于REST是无状态的，后端应用发布的REST API可在用户未登录的情况下被任意调用，这显然是不安全的，如何解决这个问题呢？我们需要为REST请求提供安全机制。</p>
<hr>
<p><strong>6、提供安全机制</strong></p>
<p>　　解决REST安全调用问题，可以做得很复杂，也可以做得特简单，可按照以下过程提供REST安全机制：</p>
<p>　　(1). 当用户登录成功后，在服务端生成一个token，并将其放入内存中（可放入JVM或Redis中），同时将该token返回到客户端；</p>
<p>　　(2). 在客户端中将返回的token写入cookie中，并且每次请求时都将token随请求头一起发送到服务端；</p>
<p>　　(3). 提供一个AOP切面，用于拦截所有的Controller方法，在切面中判断token的有效性；</p>
<p>　　(4). 当登出时，只需清理掉cookie中的token即可，服务端token可设置过期时间，使其自行移除。</p>
<p>　　首先，我们需要定义一个用于管理token的接口，包括创建token与检查token有效性的功能。代码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;**        </span><br><span class="line"> * Title: REST 鉴权   </span><br><span class="line"> * Description: 登录用户的身份鉴权</span><br><span class="line"> * @author rico       </span><br><span class="line"> * @created 2017年7月4日 下午4:41:43    </span><br><span class="line"> *&#x2F;      </span><br><span class="line">public interface TokenManager &#123;</span><br><span class="line"></span><br><span class="line">    String createToken(String username);  </span><br><span class="line"></span><br><span class="line">    boolean checkToken(String token); </span><br><span class="line"></span><br><span class="line">    void deleteToken(String token);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后，我们可提供一个简单的TokenManager实现类，将token存储到JVM内存中。代码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;**        </span><br><span class="line"> * Title: TokenManager的默认实现    </span><br><span class="line"> * Description: 管理 Token</span><br><span class="line"> * @author rico       </span><br><span class="line"> * @created 2017年7月4日 下午4:41:32    </span><br><span class="line"> *&#x2F;      </span><br><span class="line">public class DefaultTokenManager implements TokenManager &#123;</span><br><span class="line"></span><br><span class="line">    &#x2F;** 将token存储到JVM内存(ConcurrentHashMap)中   (@author: rico) *&#x2F;      </span><br><span class="line">    private static Map&lt;String, String&gt; tokenMap &#x3D; new ConcurrentHashMap&lt;String, String&gt;();</span><br><span class="line"></span><br><span class="line">    &#x2F;** </span><br><span class="line">     * @description 利用UUID创建Token(用户登录时，创建Token)</span><br><span class="line">     * @author rico       </span><br><span class="line">     * @created 2017年7月4日 下午4:46:46      </span><br><span class="line">     * @param username</span><br><span class="line">     * @return     </span><br><span class="line">     * @see cn.edu.tju.rico.authorization.TokenManager#createToken(java.lang.String)     </span><br><span class="line">     *&#x2F;  </span><br><span class="line">    public String createToken(String username) &#123;</span><br><span class="line">        String token &#x3D; CodecUtil.createUUID();</span><br><span class="line">        tokenMap.put(token, username);</span><br><span class="line">        return token;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &#x2F;** </span><br><span class="line">     * @description Token验证(用户登录验证)</span><br><span class="line">     * @author rico       </span><br><span class="line">     * @created 2017年7月4日 下午4:46:50      </span><br><span class="line">     * @param token</span><br><span class="line">     * @return     </span><br><span class="line">     * @see cn.edu.tju.rico.authorization.TokenManager#checkToken(java.lang.String)     </span><br><span class="line">     *&#x2F;  </span><br><span class="line">    public boolean checkToken(String token) &#123;</span><br><span class="line">        return !StringUtil.isEmpty(token) &amp;&amp; tokenMap.containsKey(token);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &#x2F;** </span><br><span class="line">     * @description Token删除(用户登出时，删除Token)</span><br><span class="line">     * @author rico       </span><br><span class="line">     * @created 2017年7月4日 下午4:46:54      </span><br><span class="line">     * @param token     </span><br><span class="line">     * @see cn.edu.tju.rico.authorization.TokenManager#deleteToken(java.lang.String)     </span><br><span class="line">     *&#x2F;  </span><br><span class="line">    @Override</span><br><span class="line">    public void deleteToken(String token) &#123;</span><br><span class="line">        &#x2F;&#x2F; TODO Auto-generated method stub</span><br><span class="line">        tokenMap.remove(token);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>需要注意的是，如果需要做到分布式集群，建议基于Redis提供一个实现类，将token存储到Redis中，并利用Redis与生俱来的特性，做到token的分布式一致性。 </p>
<p>然后，我们可以基于Spring AOP写一个切面类，用于拦截Controller类的方法，并从请求头中获取token，最后对token有效性进行判断。代码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;**</span><br><span class="line"> * Title:安全检查切面(是否登录检查) </span><br><span class="line"> * Description: 通过验证Token维持登录状态</span><br><span class="line"> * </span><br><span class="line"> * @author rico</span><br><span class="line"> * @created 2017年7月4日 下午4:32:34</span><br><span class="line"> *&#x2F;</span><br><span class="line">@Component</span><br><span class="line">@Aspect</span><br><span class="line">public class SecurityAspect &#123;</span><br><span class="line"></span><br><span class="line">    &#x2F;** Log4j日志处理(@author: rico) *&#x2F;</span><br><span class="line">    private static final Logger log &#x3D; Logger.getLogger(SecurityAspect.class);</span><br><span class="line"></span><br><span class="line">    private TokenManager tokenManager;</span><br><span class="line"></span><br><span class="line">    @Resource(name &#x3D; &quot;tokenManager&quot;)</span><br><span class="line">    public void setTokenManager(TokenManager tokenManager) &#123;</span><br><span class="line">        this.tokenManager &#x3D; tokenManager;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Around(&quot;@annotation(org.springframework.web.bind.annotation.RequestMapping)&quot;)</span><br><span class="line">    public Object execute(ProceedingJoinPoint pjp) throws Throwable &#123;</span><br><span class="line">        &#x2F;&#x2F; 从切点上获取目标方法</span><br><span class="line">        MethodSignature methodSignature &#x3D; (MethodSignature) pjp.getSignature();</span><br><span class="line">        log.debug(&quot;methodSignature : &quot; + methodSignature);</span><br><span class="line">        Method method &#x3D; methodSignature.getMethod();</span><br><span class="line">        log.debug(&quot;Method : &quot; + method.getName() + &quot; : &quot;</span><br><span class="line">                + method.isAnnotationPresent(IgnoreSecurity.class));</span><br><span class="line">        &#x2F;&#x2F; 若目标方法忽略了安全性检查,则直接调用目标方法</span><br><span class="line">        if (method.isAnnotationPresent(IgnoreSecurity.class)) &#123;</span><br><span class="line">            return pjp.proceed();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        &#x2F;&#x2F; 从 request header 中获取当前 token</span><br><span class="line">        String token &#x3D; WebContextUtil.getRequest().getHeader(</span><br><span class="line">                Constants.DEFAULT_TOKEN_NAME);</span><br><span class="line">        &#x2F;&#x2F; 检查 token 有效性</span><br><span class="line">        if (!tokenManager.checkToken(token)) &#123;</span><br><span class="line">            String message &#x3D; String.format(&quot;token [%s] is invalid&quot;, token);</span><br><span class="line">            log.debug(&quot;message : &quot; + message);</span><br><span class="line">            throw new TokenException(message);</span><br><span class="line">        &#125;</span><br><span class="line">        &#x2F;&#x2F; 调用目标方法</span><br><span class="line">        return pjp.proceed();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>若要使SecurityAspect生效，则需要在SpringMVC配置文件中添加如下Spring 配置：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 启用注解扫描，并定义组件查找规则 ，mvc层只负责扫描@Controller、@ControllerAdvice --&gt;</span><br><span class="line">    &lt;!-- base-package 如果多个，用“,”分隔 --&gt;</span><br><span class="line">    &lt;context:component-scan base-package&#x3D;&quot;cn.edu.tju.rico&quot;</span><br><span class="line">        use-default-filters&#x3D;&quot;false&quot;&gt;</span><br><span class="line">        &lt;!-- 扫描 @Controller --&gt;</span><br><span class="line">        &lt;context:include-filter type&#x3D;&quot;annotation&quot;</span><br><span class="line">            expression&#x3D;&quot;org.springframework.stereotype.Controller&quot; &#x2F;&gt;</span><br><span class="line">        &lt;!-- 控制器增强，使一个Contoller成为全局的异常处理类，类中用@ExceptionHandler方法注解的方法可以处理所有Controller发生的异常 --&gt;</span><br><span class="line">        &lt;context:include-filter type&#x3D;&quot;annotation&quot;</span><br><span class="line">            expression&#x3D;&quot;org.springframework.web.bind.annotation.ControllerAdvice&quot; &#x2F;&gt;</span><br><span class="line">    &lt;&#x2F;context:component-scan&gt;</span><br><span class="line"></span><br><span class="line">    &lt;!-- 支持Controller的AOP代理 --&gt;</span><br><span class="line">    &lt;aop:aspectj-autoproxy &#x2F;&gt;</span><br></pre></td></tr></table></figure>

<p>最后，别忘了在web.xml中添加允许的X-Token响应头，配置如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&lt;init-param&gt;  </span><br><span class="line">    &lt;param-name&gt;allowHeaders&lt;&#x2F;param-name&gt;  </span><br><span class="line">    &lt;param-value&gt;Content-Type,X-Token&lt;&#x2F;param-value&gt;  </span><br><span class="line">&lt;&#x2F;init-param&gt;  </span><br></pre></td></tr></table></figure>

<h2 id="四-关于Demo部署的若干建议"><a href="#四-关于Demo部署的若干建议" class="headerlink" title="四. 关于Demo部署的若干建议"></a>四. 关于Demo部署的若干建议</h2><p>　　本项目是一个使用Maven进行构建的项目，关于Maven的了解、使用推荐大家看孤傲苍狼的<a target="_blank" rel="noopener" href="http://www.cnblogs.com/xdp-gacl/tag/Maven%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93/">《Maven学习总结》</a>一系列博客；</p>
<p>　　关于REST服务的调试推荐大家使用Postman这款工具，请大家自行下载与安装，具体见<a target="_blank" rel="noopener" href="http://www.cnblogs.com/hubgit/p/6589701.html">《postman的安装与使用（模拟请求）》</a>；</p>
<hr>
<h2 id="五-总结"><a href="#五-总结" class="headerlink" title="五. 总结"></a>五. 总结</h2><p>　　本文从经典的MVC模式开始，对MVC模式是什么以及该模式存在的不足进行了简述。然后引出了如何对MVC模式的改良，让其转变为前后端分离架构，以及解释了为何要进行前后端分离。最后通过REST服务将前后端进行解耦，并提供了一款基于Java的REST框架的主要实现过程，尤其是需要注意的核心技术问题及其解决方案。希望本文对正在探索前后端分离的读者们有所帮助，期待与大家共同探讨。 
　　</p>
<hr>
<h2 id="六-更多"><a href="#六-更多" class="headerlink" title="六. 更多"></a>六. 更多</h2><p>本项目的全部完整源码可以在我的GitHub上找到，项目名为RestSpringMVCDemo，项目地址为：<a target="_blank" rel="noopener" href="https://github.com/githubofrico/RestSpringMVCDemo%E3%80%82">https://github.com/githubofrico/RestSpringMVCDemo。</a></p>
<p>更多关于REST的介绍，请移步我的博文<a target="_blank" rel="noopener" href="http://blog.csdn.net/justloveyou_/article/details/73612999">《理解RESTful架构》</a>。</p>
<p>更多关于 Java Web 方面的内容，请关注我的专栏 <a target="_blank" rel="noopener" href="http://static.zybuluo.com/Rico123/et4q5o2ok4ryi6dniwifvcx0/MVC.jpg">《Java Web 成神之路》</a>。本专栏全面记录了Java Web开发相关知识，不但包括对http, servlet,session等基础知识的讲解，还包括对流行框架(SSM,SpringMVC等)、中间件(Redis等)等进阶知识的深入分析。笔者将持续跟进最新Web技术，期望对大家能够起到抛砖引玉的效果。</p>
<h2 id="本文转载自：https-www-cnblogs-com-softidea-p-8144890-html"><a href="#本文转载自：https-www-cnblogs-com-softidea-p-8144890-html" class="headerlink" title="本文转载自：https://www.cnblogs.com/softidea/p/8144890.html"></a>本文转载自：<a target="_blank" rel="noopener" href="https://www.cnblogs.com/softidea/p/8144890.html">https://www.cnblogs.com/softidea/p/8144890.html</a></h2>
          
        
      
    </div>
    
    
    
    
    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://joy_24.gitee.io/myblogs/2020/11/25/ideaUseTutorial/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/myblogs/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="天行健の博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/myblogs/2020/11/25/ideaUseTutorial/" itemprop="url">IDEA使用教程</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-11-25T15:08:18+08:00">
                2020-11-25
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/myblogs/categories/IDE/" itemprop="url" rel="index">
                    <span itemprop="name">IDE</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  2.5k
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  9
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <a id="more"></a>     #首页只会显示标记以前的内容，标记后的内容可以通过阅读更多查阅

<p>自己使用IDEA时整理了一些教程，在这里保存下来</p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319131312929.png"></p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319131326117.png"></p>
<p>注意：这里面 web app 的骨架有2个，需要匹配第二个才是需要的 ，所以需要注意。</p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319131503135.png"></p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319131520213.png"></p>
<p>给自己的项目的本地仓库，默认是在c盘的.m2文件夹下，</p>
<p>但是<strong>这很是占用**</strong>c**<strong>盘本来就不算大的系统盘的地方</strong>。</p>
<p>所以这里可以这么配置就可以把本地仓库移动到c盘之外的任意地方。就这么做就可以啦。</p>
<p>另外使用的是国内的阿里的仓库。下载速度快。</p>
<p>参考文献：</p>
<p>1，[ maven 本地仓库的配置以及如何修改默认.m2仓库位置](onenote:#maven 本地仓库的配置以及如何修改默认.m2仓库位置&amp;section-id={0BF884F1-BD40-4B89-B625-C173B78E0670}&amp;page-id={4A0D6CAE-3032-42CC-8632-33908F5B360C}&amp;end&amp;base-path=<a target="_blank" rel="noopener" href="https://d.docs.live.net/2fd66831a7ab4b8d/%E6%96%87%E6%A1%A3/%E5%B7%A5%E4%BD%9C%E7%AC%94%E8%AE%B0%E6%9C%AC/%E7%9F%A5%E8%AF%86%E8%AE%B0%E5%BD%95%E6%9C%AC.one">https://d.docs.live.net/2fd66831a7ab4b8d/文档/工作笔记本/知识记录本.one</a>)</p>
<p>2，[maven 阿里云 国内镜像 中央仓库 亲测可用](onenote:#maven 阿里云 国内镜像 中央仓库 亲测可用&amp;section-id={0BF884F1-BD40-4B89-B625-C173B78E0670}&amp;page-id={EFFA9D40-0EB4-420C-B273-9D431A08C2DA}&amp;end&amp;base-path=<a target="_blank" rel="noopener" href="https://d.docs.live.net/2fd66831a7ab4b8d/%E6%96%87%E6%A1%A3/%E5%B7%A5%E4%BD%9C%E7%AC%94%E8%AE%B0%E6%9C%AC/%E7%9F%A5%E8%AF%86%E8%AE%B0%E5%BD%95%E6%9C%AC.one">https://d.docs.live.net/2fd66831a7ab4b8d/文档/工作笔记本/知识记录本.one</a>)</p>
<p>上面截图上说的有点不地道，不能一个项目使用一个仓库，这样的话重复的东西会太多，是我表述不清楚了，其实目的是，节约c盘的地方。可以按照上面的链接把这个仓库按照到c盘之外的其他地方就OK啦。</p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319132126621.png"></p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319132141059.png"></p>
<p>这个时候，可以看到，图中蓝色的地址，看到aliyun这个词了吧，就是阿里整的这个国内镜像仓库吧。下载速度还是很快的，嗖嗖嗖就好啦。</p>
<p>点击图上的<strong>“Import Changes”</strong>，因为是maven项目，所以<strong>当依赖包发生变化时</strong>，也就是<strong>pom.xml**</strong>文件有修改的时候。**</p>
<p>他就得重新检查下包的依赖，没有的，自己会去下载的。这个要记住啦，因为maven项目的话，这个基本是天天见的，要熟悉。</p>
<p>执行完之后，你可以去看，<strong>你刚刚新建的那个本地仓库的那个空文件夹，发现你需要的项目依赖的各种**</strong>jar**<strong>包都下载下来啦。</strong></p>
<p>如果一切都OK的话，那么下面就该准备tomcat啦。</p>
<p>继续看图。</p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319132751000.png"></p>
<p>到这之后，有的同学，反应：<strong>“**</strong>我的编辑器这，怎么没有这一行呢？**<strong>”</strong></p>
<p>请看下面链接，如何把这个给搞出来。hello world项目跑完，顺带学习一下怎么使用这个编辑器，也是极好的。</p>
<p>[IntelliJ IDEA中如何显示和关闭—-工具栏，目录栏](onenote:#IntelliJ IDEA中如何显示和关闭—-工具栏，目录栏&amp;section-id={0BF884F1-BD40-4B89-B625-C173B78E0670}&amp;page-id={6C0F7BA1-0A1B-4884-929D-C7216AC5AA41}&amp;end&amp;base-path=<a target="_blank" rel="noopener" href="https://d.docs.live.net/2fd66831a7ab4b8d/%E6%96%87%E6%A1%A3/%E5%B7%A5%E4%BD%9C%E7%AC%94%E8%AE%B0%E6%9C%AC/%E4%B8%B4%E6%97%B6%E8%AE%B0%E5%BD%95%E6%9C%AC.one">https://d.docs.live.net/2fd66831a7ab4b8d/文档/工作笔记本/临时记录本.one</a>)</p>
<p>这一行里面的按钮还是比较常用的。</p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319132800764.png"></p>
<p><strong>注意，是点那个加号，再去配置的，而不是直接点下面的。</strong></p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319132814594.png"></p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319132840344.png"></p>
<p>估计有的小伙伴们到这个地方，哎，发现，我怎么就只有一个选项呀，<strong>artifact**</strong>这个选项怎么没有呢？**</p>
<p>不要惊慌，</p>
<p>方法一：</p>
<p>先看文章最底部的更新内容，看看是不是因为安装的编辑器不是开发版，也就是说你安装的是免费版的，那可能就是少了下面的插件。</p>
<p>看看安装之后，是不是就可以搞定问题啦。</p>
<p>后经有同学反应，你搜索不到插件的，是因为你使用的阉割版的，建议安装专业版，也就是付费的。</p>
<p>方法二：</p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170629101346633.png"></p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170629101401439.png"></p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170629101415530.png"></p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170629101425730.png"></p>
<p>到这，你就可以添加了，或者直接点右下角的那个fix，估计一下就自动，给搞定了，继续吧。</p>
<p>**更新完毕。</p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319132853094.png"></p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319132905140.png"></p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319132945079.png"></p>
<p>在 Java Web 开发中， 一般更新了 Java 文件后要手动重启 Tomcat 服务器， 才能生效， 有印象没？我是真的遇到过，当时用的编辑器是eclipse，使用的服务器好像是jboss，浪费不少生命啊， 你如上配置的话， 不论是更新 class 类，css文件，html文件，js文件，还是更新 Spring 配置文件都能做到立马生效，大大提高开发效率。</p>
<p>我最下面示范的，我说一刷新页面，就可以看到我修改后的效果，就是简单给你演示下热部署功能的好处。</p>
<p>给你省去了重启tomcat的时间。</p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319133106439.png"></p>
<p>箭头指向的这一栏，这个是你配置tomcat之后，才有的，OK？</p>
<p>这个也许你知道呢，但是我就爱啰嗦。</p>
<p>debug就是启动的时候，你可以直接在代码里面点个断点就可以debug调试啦，你要是直接启动，那么你就不能debug调试啦。</p>
<p>当然啦在实际开发中，一般都是debug模式启动的，因为你开发过程中要调试各种代码和bug。</p>
<p>所以，debug启动在开发过程使用的是很多的。</p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319133334895.png"></p>
<p>自动弹出个网页，是因为你在上面配置tomcat的时候，自己配置的，造吗？回头看一下，有个open browse after launch，配置打开的还是chrome浏览器。</p>
<p>chrome浏览器，是web开发建议使用的浏览器。</p>
<p>至此，这个<strong>创建**</strong>maven** <strong>管理的**</strong>Java web**<strong>项目的**</strong>hello world**就算完成 了。</p>
<p>你要是掌握了这个，那么<strong>基本上**</strong>Java web**<strong>开发都是这么个套路</strong>，起码<strong>创建**</strong>tomcat**<strong>这个套路</strong>就是这么滴。学会这个就很好啦。</p>
<p>下面再 给看看新建的项目的文件目录结构的样子。</p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319133529351.png"></p>
<p>有小伙伴表示，我怎么没有target这个文件夹呢？</p>
<p>表着急，这个是web项目运行之后，把东西打包到这个地方的。自动生成的，你项目页面还没跑起来，先表激动为啥我没有这个文件夹.</p>
<p>我表示，我现在基本上天天看的都是这么个界面吧，大同小异啦。</p>
<p>所以，作为观众的你，提前熟悉一下，也是没什么坏处的咯。</p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319133634414.png"></p>
<p><strong>这个我就是给你示范一下，实际开发中，为什么要在**</strong>tomcat**<strong>里面那么配置个热部署的功能吧算是。</strong></p>
<p>因为你在开发的时候，你修改个页面的文字啥的，总是去停了再启动一下tomcat，太啰嗦。</p>
<p>所以，你按我上面配置tomcat的姿势，配置好了tomcat之后，修改完之后，只要刷新下浏览器页面，就可以发现，刚刚做的修改，在没有重启tomcat的情况下 ，就可看到效果。这就完美的解决“<strong>动不动就重启下**</strong>tomcat**<strong>服务器，浪费时间</strong>”的这个重复的问题，多好。</p>
<p>我说的这些，可能你花钱去培训也不一定知道哦。</p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319133912521.png"></p>
<p>这张图，<strong>是我在**</strong>index.jsp**<strong>文件上添加了汉字之后，没有重启**</strong>tomcat**<strong>，直接在浏览器页面，刷新一下，就看到了效果啦。</strong></p>
<p>呵呵，小意外。乱码了。。。。。。。。。。。</p>
<p>虽然，你看到我的页面上出了乱码，但是实际开发过程中，你也可能出现这个情况，现在我告诉你怎么解决，其实就这一张图就是一个问题。</p>
<p>就是你的网页上某个地方乱码啦，你怎么去处理。</p>
<p>因为我没有对这个页面的编码方式进行设置，所以，就 乱码啦，具体解决方案就看下一张图。</p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319134101405.png"></p>
<p>我又在文件上添加了一行代码，然后再次刷新下浏览器页面，OK。</p>
<p>页面上的汉字就显示正常啦。 </p>
<p>把用到的这句话放这儿吧，估计看官的你也懒得敲。就想着复制粘贴呢。还不知道你。。。。哼。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;%@ page language&#x3D;&quot;java&quot; contentType&#x3D;&quot;text&#x2F;html; charset&#x3D;UTF-8&quot; pageEncoding&#x3D;&quot;UTF-8&quot;%&gt;</span><br></pre></td></tr></table></figure>

<p>这下看到了，我在实际开发中也出现过类似的问题，就是某个模块，就是乱码，不知道为啥，当然，也是浪费了点时间，知道在页面上没有设置编码格式，所以才会乱码的，那么你现在看到我告诉你如何解决这个问题啦，你要是在实际工作中也遇到这个问题的话，那么，你就可以很轻松的解决这个问题。这个也是极好的啦。</p>
<p>下面说，我的那个<strong>maven**</strong>的配置文件的内容**。主要里面就配置了2点。</p>
<p>1，就是把本地仓库的地址，自定义到我的e盘的某个文件夹下。</p>
<p>2，配置国内的镜像仓库地址，使用阿里云的maven镜像仓库。</p>
<p>具体内容如下：</p>
<p>如果你不想去看上面的2个链接的话：直接复制下面内容，然后再修改你自己电脑上的某个文件夹作为你的本地仓库地址就可以啦。</p>
<p>哎，算了，太长了。我就只贴出来关键代码，你自己去你安装的maven的文件目录下复制个setting.xml文件过来修改吧。我就不整个贴啦。</p>
<p>还是看图吧：</p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319134907543.png"></p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319134920137.png"></p>
<p><img src="/myblogs/2020/11/25/ideaUseTutorial/20170319134930167.png"></p>
<p>关于图中的镜像仓库的地址的文字如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;mirror&gt;    </span><br><span class="line">	&lt;id&gt;alimaven&lt;&#x2F;id&gt;    </span><br><span class="line">	&lt;name&gt;aliyun maven&lt;&#x2F;name&gt;    </span><br><span class="line">	&lt;url&gt;http:&#x2F;&#x2F;maven.aliyun.com&#x2F;nexus&#x2F;content&#x2F;groups&#x2F;public&#x2F;&lt;&#x2F;url&gt;    </span><br><span class="line">	&lt;mirrorOf&gt;central&lt;&#x2F;mirrorOf&gt;            </span><br><span class="line">&lt;&#x2F;mirror&gt;</span><br></pre></td></tr></table></figure>

<p>这个配置文件以及配置文件里面配置的本地仓库的地址，都是我们在新建mavne项目的时候，使用的，可以回头看看我刚刚新建项目的时候，是不是用的就是这个文件，以及这个本地仓库地址。</p>

          
        
      
    </div>
    
    
    
    
    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://joy_24.gitee.io/myblogs/2020/11/13/idea-hotKey/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/myblogs/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="天行健の博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/myblogs/2020/11/13/idea-hotKey/" itemprop="url">IDEA常用快捷键列表</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-11-13T14:59:08+08:00">
                2020-11-13
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/myblogs/categories/IDE/" itemprop="url" rel="index">
                    <span itemprop="name">IDE</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  1.1k
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  4
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h1 id="———–自动代码——–"><a href="#———–自动代码——–" class="headerlink" title="———–自动代码——–"></a>———–自动代码——–</h1><p>常用的有fori/sout/psvm+Tab即可生成循环、System.out、main方法等boilerplate样板代码 </p>
<p>例如要输入for(User user : users)只需输入user.for+Tab </p>
<p>再比如，要输入Date birthday = user.getBirthday();只需输入user.getBirthday().var+Tab即可。代码标签输入完成后，按Tab，生成代码。</p>
<p>Ctrl+Alt+O 优化导入的类和包 </p>
<p>Alt+Insert 生成代码(如get,set方法,构造函数等)  或者右键（Generate） </p>
<p>fori/sout/psvm + Tab </p>
<p>Ctrl+Alt+T  生成try catch  或者 Alt+enter </p>
<p>CTRL+ALT+T 把选中的代码放在 TRY{} IF{} ELSE{} 里 </p>
<p>Ctrl + O 重写方法 </p>
<p>Ctrl + I 实现方法 </p>
<p>Ctr+shift+U 大小写转化 </p>
<p>ALT+回车   导入包,自动修正 </p>
<p>ALT+/    代码提示 </p>
<p>CTRL+J    自动代码 </p>
<p>Ctrl+Shift+J，整合两行为一行 </p>
<p>CTRL+空格  代码提示 </p>
<p>CTRL+SHIFT+SPACE 自动补全代码 </p>
<p>CTRL+ALT+L  格式化代码 </p>
<p>CTRL+ALT+I 自动缩进 </p>
<p>CTRL+ALT+O  优化导入的类和包 </p>
<p>ALT+INSERT 生成代码(如GET,SET方法,构造函数等) </p>
<p>CTRL+E    最近更改的代码 </p>
<p>CTRL+ALT+SPACE 类名或接口名提示 </p>
<p>CTRL+P  方法参数提示 </p>
<p>CTRL+Q，可以看到当前方法的声明 </p>
<p>Shift+F6  重构-重命名 (包、类、方法、变量、甚至注释等) </p>
<p>Ctrl+Alt+V 提取变量 </p>
<p>Alt + Shift + Insert 鼠标选中是一个矩形区域，或者：菜单栏Edit 选择Column Selection Mode</p>
<h1 id="———–查询快捷键——–"><a href="#———–查询快捷键——–" class="headerlink" title="———–查询快捷键——–"></a>———–查询快捷键——–</h1><p>Ctrl＋Shift＋Backspace可以跳转到上次编辑的地 </p>
<p>CTRL+ALT+ left/right 前后导航编辑过的地方 </p>
<p>ALT+7 靠左窗口显示当前文件的结构 </p>
<p>Ctrl+F12 浮动显示当前文件的结构 </p>
<p>ALT+F7 找到你的函数或者变量或者类的所有引用到的地方 </p>
<p>CTRL+ALT+F7 找到你的函数或者变量或者类的所有引用到的地方 </p>
<p>Ctrl+Shift+Alt+N 查找类中的方法或变量 </p>
<p>双击SHIFT 在项目的所有目录查找文件 </p>
<p>Ctrl+N  查找类 </p>
<p>Ctrl+Shift+N 查找文件 </p>
<p>CTRL+G  定位行 </p>
<p>CTRL+F  在当前窗口查找文本 </p>
<p>CTRL+SHIFT+F 在指定窗口查找文本 </p>
<p>CTRL+R  在 当前窗口替换文本 </p>
<p>CTRL+SHIFT+R 在指定窗口替换文本 </p>
<p>ALT+SHIFT+C 查找修改的文件 </p>
<p>CTRL+E  最近打开的文件 </p>
<p>F3  向下查找关键字出现位置 </p>
<p>SHIFT+F3 向上一个关键字出现位置 </p>
<p>选中文本，按Alt+F3 ，高亮相同文本，F3逐个往下查找相同文本 </p>
<p>F4  查找变量来源 </p>
<p>Ctrl + \ 通过restfulToolKit插件查找后台controller</p>
<p>CTRL+SHIFT+O 弹出显示查找内容 </p>
<p>Ctrl+W 选中代码，连续按会有其他效果 </p>
<p>F2 或Shift+F2 高亮错误或警告快速定位 </p>
<p>Ctrl+Up/Down 光标跳转到第一行或最后一行下 </p>
<p>Ctrl+B 快速打开光标处的类或方法 </p>
<p>CTRL+ALT+B 找所有的子类 </p>
<p>CTRL+SHIFT+B 找变量的类 </p>
<p>Ctrl+Shift+上下键 上下移动代码 </p>
<p>Ctrl+Alt+ left/right 返回至上次浏览的位置 </p>
<p>Ctrl+X 删除行 </p>
<p>Ctrl+D 复制行 </p>
<p>Ctrl+/ 或 Ctrl+Shift+/ 注释（// 或者/<em>…</em>/ ） </p>
<p>Ctrl+H 显示类结构图 </p>
<p>Ctrl+Q 显示注释文档 </p>
<p>Alt+F1 查找代码所在位置 </p>
<p>Alt+1 快速打开或隐藏工程面板 </p>
<p>Alt+ left/right 切换代码视图 </p>
<p>ALT+ ↑/↓ 在方法间快速移动定位 </p>
<p>CTRL+ALT+ left/right 前后导航编辑过的地方 </p>
<p>Ctrl＋Shift＋Backspace可以跳转到上次编辑的地 </p>
<p>Alt+6   查找TODO </p>
<h1 id="———————其他快捷键——————"><a href="#———————其他快捷键——————" class="headerlink" title="———————其他快捷键——————-"></a>———————其他快捷键——————-</h1><p>SHIFT+ENTER 另起一行 </p>
<p>CTRL+Z  倒退(撤销) </p>
<p>CTRL+SHIFT+Z 向前(取消撤销) </p>
<p>CTRL+ALT+F12 资源管理器打开文件夹 </p>
<p>ALT+F1  查找文件所在目录位置 </p>
<p>SHIFT+ALT+INSERT 竖编辑模式 </p>
<p>CTRL+F4 关闭当前窗口 </p>
<p>Ctrl+Alt+V，可以引入变量。例如：new String(); 自动导入变量定义 </p>
<p>Ctrl+~，快速切换方案（界面外观、代码风格、快捷键映射等菜单） </p>
<h1 id="————–svn快捷键—————"><a href="#————–svn快捷键—————" class="headerlink" title="————–svn快捷键—————"></a>————–svn快捷键—————</h1><p>ctrl+k 提交代码到SVN </p>
<p>ctrl+t 更新代码 </p>
<h1 id="————–调试快捷键—————"><a href="#————–调试快捷键—————" class="headerlink" title="————–调试快捷键—————"></a>————–调试快捷键—————</h1><p><strong>其实常用的 就是F8 F7 F9 最值得一提的 就是Drop Frame  可以让运行过的代码从头再来</strong></p>
<p>alt+F8      debug时选中查看值 </p>
<p>Alt+Shift+F9，选择 Debug </p>
<p>Alt+Shift+F10，选择 Run </p>
<p>Ctrl+Shift+F9，编译 </p>
<p>Ctrl+Shift+F8，查看断点 </p>
<p>F7，步入 </p>
<p>Shift+F7，智能步入 </p>
<p>Alt+Shift+F7，强制步入 </p>
<p>F8，步过 </p>
<p>Shift+F8，步出 </p>
<p>Alt+Shift+F8，强制步过 </p>
<p>Alt+F9，运行至光标处 </p>
<p>Ctrl+Alt+F9，强制运行至光标处 </p>
<p>F9，恢复程序 </p>
<p>Alt+F10，定位到断点 </p>
<h1 id="————–重构—————"><a href="#————–重构—————" class="headerlink" title="————–重构—————"></a>————–重构—————</h1><p>Ctrl+Alt+Shift+T，弹出重构菜单 </p>
<p>Shift+F6，重命名 </p>
<p>F6，移动 </p>
<p>F5，复制 </p>
<p>Alt+Delete，安全删除 </p>
<p>Ctrl+Alt+N，内联 </p>
<p>=======================================================</p>

          
        
      
    </div>
    
    
    
    
    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://joy_24.gitee.io/myblogs/2020/11/13/linuxCommand1/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/myblogs/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="天行健の博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/myblogs/2020/11/13/linuxCommand1/" itemprop="url">Linux常用命令</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-11-13T11:44:01+08:00">
                2020-11-13
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/myblogs/categories/Linux/" itemprop="url" rel="index">
                    <span itemprop="name">Linux</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  1.1k
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  4
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h6 id="1、pwd：显示当前工作目录"><a href="#1、pwd：显示当前工作目录" class="headerlink" title="1、pwd：显示当前工作目录"></a>1、pwd：显示当前工作目录</h6><hr>
<h6 id="2、ls：查看当前工作目录的内容"><a href="#2、ls：查看当前工作目录的内容" class="headerlink" title="2、ls：查看当前工作目录的内容"></a>2、ls：查看当前工作目录的内容</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">ls -F 查看目录中的文件 </span><br><span class="line"></span><br><span class="line">ls -l 显示文件和目录的详细资料 </span><br><span class="line"></span><br><span class="line">ls -a 显示隐藏文件 </span><br><span class="line"></span><br><span class="line">ls -ltr：按时间排序查询 </span><br></pre></td></tr></table></figure>

<hr>
<h6 id="3、cd：改变工作目录"><a href="#3、cd：改变工作目录" class="headerlink" title="3、cd：改变工作目录"></a>3、cd：改变工作目录</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">例如使用pwd查看当前工作目录为home&#x2F;d5000&#x2F;var&#x2F;osa</span><br><span class="line"></span><br><span class="line">通过ls命令查看该目录下有一个目录名为rtnet</span><br><span class="line"></span><br><span class="line">则输入cd rtnet即可进入rtnet目录</span><br><span class="line"></span><br><span class="line">cd ..&#x2F;..&#x2F;——返回上上级目录</span><br></pre></td></tr></table></figure>

<hr>
<h6 id="4、cp：复制命令"><a href="#4、cp：复制命令" class="headerlink" title="4、cp：复制命令"></a>4、cp：复制命令</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cp a b——a是原文件，b是新生成的副本文件</span><br><span class="line"></span><br><span class="line">cp -r c d——c是原目录，d是新生成的副本目录，含目录中的全部内容</span><br></pre></td></tr></table></figure>



<h6 id="5、mv：移动文件或改名"><a href="#5、mv：移动文件或改名" class="headerlink" title="5、mv：移动文件或改名"></a>5、mv：移动文件或改名</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mv setvalue.log &#x2F;home&#x2F;d5000&#x2F;var&#x2F;log——把setvalue.log移动到&#x2F;home&#x2F;d5000&#x2F;var&#x2F;log中</span><br><span class="line"></span><br><span class="line">mv setvalue.log setvalue.log.bak——把setvalue.log改名为setvalue.log.bak</span><br></pre></td></tr></table></figure>



<h6 id="6、rm：删除命令"><a href="#6、rm：删除命令" class="headerlink" title="6、rm：删除命令"></a>6、rm：删除命令</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">rm *.o——删除所有.o文件，*为通配符</span><br><span class="line"></span><br><span class="line">rm -f file1 删除一个叫做 &#39;file1&#39; 的文件&#39; </span><br><span class="line"></span><br><span class="line">rmdir dir1 删除一个叫做 &#39;dir1&#39; 的目录&#39; </span><br><span class="line"></span><br><span class="line">rm -rf dir1 删除一个叫做 &#39;dir1&#39; 的目录并同时删除其内容 </span><br><span class="line"></span><br><span class="line">rm -rf dir1 dir2 同时删除两个目录及它们的内容</span><br></pre></td></tr></table></figure>



<h6 id="7、mkdir：在当前目录下创建目录"><a href="#7、mkdir：在当前目录下创建目录" class="headerlink" title="7、mkdir：在当前目录下创建目录"></a>7、mkdir：在当前目录下创建目录</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">mkdir test——在当前目录创建一个名为test的目录</span><br><span class="line"></span><br><span class="line">mkdir dir1 dir2 同时创建两个目录</span><br><span class="line"></span><br><span class="line">mkdir -p &#x2F;tmp&#x2F;dir1&#x2F;dir2 创建一个目录树</span><br></pre></td></tr></table></figure>



<h6 id="8、chmod：改变模式"><a href="#8、chmod：改变模式" class="headerlink" title="8、chmod：改变模式"></a>8、chmod：改变模式</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">chmod 777 pas_dpf.sh——将pas_dpf.sh文件的权限改为所有人可读可写执行。</span><br><span class="line"></span><br><span class="line">Chmod 000 pas_dpf.sh——讲pas_dpf.sh文件的权限改为所有人不可读不可写。</span><br></pre></td></tr></table></figure>



<h6 id="9、tar：打包-解包命令，将一个目录或几个文件打包成一个文件"><a href="#9、tar：打包-解包命令，将一个目录或几个文件打包成一个文件" class="headerlink" title="9、tar：打包/解包命令，将一个目录或几个文件打包成一个文件"></a>9、tar：打包/解包命令，将一个目录或几个文件打包成一个文件</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">tar -cvf osa.tar osa——将osa目录打包为osa.tar文件</span><br><span class="line"></span><br><span class="line">tar -xvf osa.tar——将osa.tar文件解包</span><br></pre></td></tr></table></figure>



<h6 id="10、vi：编辑文本文件"><a href="#10、vi：编辑文本文件" class="headerlink" title="10、vi：编辑文本文件"></a>10、vi：编辑文本文件</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vi setvalue.log ——编辑setvalue.log文件</span><br></pre></td></tr></table></figure>



<h6 id="11、gzip：压缩命令"><a href="#11、gzip：压缩命令" class="headerlink" title="11、gzip：压缩命令"></a>11、gzip：压缩命令</h6><p>gzip osa.tar 压缩osa.tar文件，将得到一个osa.tar.gz文件</p>
<h6 id="12、gunzip：解压缩命令"><a href="#12、gunzip：解压缩命令" class="headerlink" title="12、gunzip：解压缩命令"></a>12、gunzip：解压缩命令</h6><p>gunzip osa.tar.gz 解压缩osa.tar.gz文件，将得到一个osa.tar</p>
<h6 id="12、压缩zip与解压缩zip"><a href="#12、压缩zip与解压缩zip" class="headerlink" title="12、压缩zip与解压缩zip"></a>12、压缩zip与解压缩zip</h6><p>实例：压缩服务器上当前目录的内容为xxx.zip文件</p>
<p>zip -r xxx.zip ./*</p>
<p>解压zip文件到当前目录</p>
<p>unzip filename.zip</p>
<h6 id="13、ssh：登陆其他服务器"><a href="#13、ssh：登陆其他服务器" class="headerlink" title="13、ssh：登陆其他服务器"></a>13、ssh：登陆其他服务器</h6><p>假设当前在scd1节点（192.168.1.21），需要登陆到pas1节点（192.168.1.31）</p>
<p>有两种方法：</p>
<p>（1）ssh pas1</p>
<p>（2）ssh 192.168.1.31</p>
<p>前提是知道目标主机密码</p>
<h6 id="14、scp远程拷贝"><a href="#14、scp远程拷贝" class="headerlink" title="14、scp远程拷贝"></a>14、scp远程拷贝</h6><p>例如将源码机中的程序同步到pas1节点</p>
<p>scp rtnet pas1:/home/d5000/bin</p>
<p>在sgscd1端，将sgscd1中的emsui.jar文件拷贝到sgpc01工作站上的对应目录中，例如福建</p>
<p>//RZ sgscd1:/home/d5000/fujian/bin/mmiexec/jars % scp -p emsui.jar sgpc01:~/bin/mmiexec/jars</p>
<p>解释：</p>
<p>（1）p代表连同权限一起拷贝 </p>
<p>（2）~代表/home/d5000/fujian</p>
<p>（3）如果要拷贝目录的话，就在p前面加上r，</p>
<p>如拷贝jars目录：//RZ sgscd1:/home/d5000/fujian/bin/mmiexec % scp -rp jars sgpc01:/home/d5000/fujian/bin/mmiexec</p>
<p>（4）拷贝多个文件方法，只需要把多个文件用空格隔开。如下：拷贝emsui.jar和emsui.jar_150519_bf文件。</p>
<p>//RZ sgscd1:/home/d5000/fujian/bin/mmiexec/jars % scp -p emsui.jar emsui.jar_150519_bf sgpc01:~/bin/mmiexec/jars</p>
<h6 id="15、su：切换当前用户"><a href="#15、su：切换当前用户" class="headerlink" title="15、su：切换当前用户"></a>15、su：切换当前用户</h6><p>su -root——切换到root用户</p>
<h6 id="16、shutdown："><a href="#16、shutdown：" class="headerlink" title="16、shutdown："></a>16、shutdown：</h6><p>shutdown -r now——立即重启</p>
<h6 id="17、zip：可使用加密功能"><a href="#17、zip：可使用加密功能" class="headerlink" title="17、zip：可使用加密功能"></a>17、zip：可使用加密功能</h6><p>考虑到程序在工程现场的保密，应在每日工作结束后对源程序加密。</p>
<p>先形成osa.tar</p>
<p>对生成的osa.tar使用zip -q -P osa.tar.zip oas.tar password</p>
<p>这样会得到osa.tar.zip文件</p>
<h6 id="18、unzip：可使用解密功能"><a href="#18、unzip：可使用解密功能" class="headerlink" title="18、unzip：可使用解密功能"></a>18、unzip：可使用解密功能</h6><p>unzip -q-n -P password osa.tar.zip -d /home/xxx/var</p>
<h6 id="19、find：文件收索"><a href="#19、find：文件收索" class="headerlink" title="19、find：文件收索"></a>19、find：文件收索</h6><p>find / -name file1 从 ‘/‘ 开始进入根文件系统搜索文件和目录 </p>
<p>find / -user user1 搜索属于用户 ‘user1’ 的文件和目录 </p>
<p>find /home/user1 -name *.bin 在目录 ‘/ home/user1’ 中搜索带有’.bin’ 结尾的文件 </p>
<p>find /usr/bin -type f -atime +100 搜索在过去100天内未被使用过的执行文件 </p>
<p>find /usr/bin -type f -mtime -10 搜索在10天内被创建或者修改过的文件 </p>
<p>find / -name *.rpm -exec chmod 755 ‘{}’ ; 搜索以 ‘.rpm’ 结尾的文件并定义其权限 </p>
<p>find / -xdev -name *.rpm 搜索以 ‘.rpm’ 结尾的文件，忽略光驱、捷盘等可移动设备 </p>
<h6 id="20、dump：备份"><a href="#20、dump：备份" class="headerlink" title="20、dump：备份"></a>20、dump：备份</h6><p>dump -0aj -f /tmp/home0.bak /home 制作一个 ‘/home’ 目录的完整备份 </p>
<p>dump -1aj -f /tmp/home0.bak /home 制作一个 ‘/home’ 目录的交互式备份 </p>

          
        
      
    </div>
    
    
    
    
    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://joy_24.gitee.io/myblogs/2020/11/05/blog-bug0/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/myblogs/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="天行健の博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/myblogs/2020/11/05/blog-bug0/" itemprop="url">hexo使用NexT主题出现带百分号乱码界面问题</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-11-05T11:01:11+08:00">
                2020-11-05
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/myblogs/categories/Hexo%E5%8D%9A%E5%AE%A2/" itemprop="url" rel="index">
                    <span itemprop="name">Hexo博客</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  77
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  1
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h2><p>下载next主题后启动项目访问<a target="_blank" rel="noopener" href="http://localhost:4000/%E6%8A%A5%E9%94%99">http://localhost:4000/报错</a></p>
<h2 id="报错内容"><a href="#报错内容" class="headerlink" title="报错内容"></a>报错内容</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo使用theme出现“ &#123;% extends ‘_layout.swig‘ %&#125; &#123;% import ‘_macro&#x2F;post.swig‘ as post_template %&#125;“问题</span><br></pre></td></tr></table></figure>

<h2 id="解决方法"><a href="#解决方法" class="headerlink" title="解决方法"></a>解决方法</h2><h5 id="原因是hexo在5-0之后把swig给删除了需要自己手动安装，如下："><a href="#原因是hexo在5-0之后把swig给删除了需要自己手动安装，如下：" class="headerlink" title="原因是hexo在5.0之后把swig给删除了需要自己手动安装，如下："></a>原因是hexo在5.0之后把swig给删除了需要自己手动安装，如下：</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i hexo-renderer-swig</span><br></pre></td></tr></table></figure>


          
        
      
    </div>
    
    
    
    
    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://joy_24.gitee.io/myblogs/2020/11/05/test/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/myblogs/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="天行健の博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/myblogs/2020/11/05/test/" itemprop="url">test</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-11-05T10:33:57+08:00">
                2020-11-05
              </time>
            

            

            
          </span>

          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  0
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  1
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            
          
        
      
    </div>
    
    
    
    
    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
  </section>

  
  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/myblogs/page/2/">&lt;</a><a class="page-number" href="/myblogs/">1</a><a class="page-number" href="/myblogs/page/2/">2</a><span class="page-number current">3</span><a class="page-number" href="/myblogs/page/4/">4</a><a class="extend next" rel="next" href="/myblogs/page/4/">&gt;</a>
  </nav>



          </div>
          


          

        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      

      <section class="site-overview-wrap sidebar-panel sidebar-panel-active">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image"
                src="/myblogs/uploads/avatar.jpg"
                alt="" />
            
              <p class="site-author-name" itemprop="name"></p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                #<a href="/myblogs/archives%7C%7C%20archive">
                <a href="/myblogs/archives">
              
                  <span class="site-state-item-count">34</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/myblogs/categories/index.html">
                  <span class="site-state-item-count">9</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/myblogs/tags/index.html">
                  <span class="site-state-item-count">20</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          
            <div class="feed-link motion-element">
              <a href="/myblogs/atom.xml" rel="alternate">
                <i class="fa fa-rss"></i>
                RSS
              </a>
            </div>
          

          
            <div class="links-of-author motion-element">
                
                  <span class="links-of-author-item">
                    <a href="https://github.com/joyWJ" target="_blank" title="GitHub">
                      
                        <i class="fa fa-fw fa-github"></i>GitHub</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="wangjianjoy@126.com" target="_blank" title="E-Mail">
                      
                        <i class="fa fa-fw fa-envelope"></i>E-Mail</a>
                  </span>
                
            </div>
          

          
          

          
          

          

        </div>
      </section>

      

      
        <div class="back-to-top">
          <i class="fa fa-arrow-up"></i>
          
            <span id="scrollpercent"><span>0</span>%</span>
          
        </div>
      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Joy Wang</span>

  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Mist</a> v5.1.4</div>




        







        
      </div>
    </footer>

    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  


  











  
  
    <script type="text/javascript" src="/myblogs/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/myblogs/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/myblogs/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/myblogs/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/myblogs/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/myblogs/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  

  
  
    <script type="text/javascript" src="/myblogs/lib/canvas-nest/canvas-nest.min.js"></script>
  


  


  <script type="text/javascript" src="/myblogs/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/myblogs/js/src/motion.js?v=5.1.4"></script>



  
  

  

  


  <script type="text/javascript" src="/myblogs/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  

  <script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    var isXml = true;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length === 0) {
      search_path = "search.xml";
    } else if (/json$/i.test(search_path)) {
      isXml = false;
    }
    var path = "/myblogs/" + search_path;
    // monitor main search box;

    var onPopupClose = function (e) {
      $('.popup').hide();
      $('#local-search-input').val('');
      $('.search-result-list').remove();
      $('#no-result').remove();
      $(".local-search-pop-overlay").remove();
      $('body').css('overflow', '');
    }

    function proceedsearch() {
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
        .css('overflow', 'hidden');
      $('.search-popup-overlay').click(onPopupClose);
      $('.popup').toggle();
      var $localSearchInput = $('#local-search-input');
      $localSearchInput.attr("autocapitalize", "none");
      $localSearchInput.attr("autocorrect", "off");
      $localSearchInput.focus();
    }

    // search function;
    var searchFunc = function(path, search_id, content_id) {
      'use strict';

      // start loading animation
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay">' +
          '<div id="search-loading-icon">' +
          '<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
          '</div>' +
          '</div>')
        .css('overflow', 'hidden');
      $("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');

      $.ajax({
        url: path,
        dataType: isXml ? "xml" : "json",
        async: true,
        success: function(res) {
          // get the contents from search data
          isfetched = true;
          $('.popup').detach().appendTo('.header-inner');
          var datas = isXml ? $("entry", res).map(function() {
            return {
              title: $("title", this).text(),
              content: $("content",this).text(),
              url: $("url" , this).text()
            };
          }).get() : res;
          var input = document.getElementById(search_id);
          var resultContent = document.getElementById(content_id);
          var inputEventFunction = function() {
            var searchText = input.value.trim().toLowerCase();
            var keywords = searchText.split(/[\s\-]+/);
            if (keywords.length > 1) {
              keywords.push(searchText);
            }
            var resultItems = [];
            if (searchText.length > 0) {
              // perform local searching
              datas.forEach(function(data) {
                var isMatch = false;
                var hitCount = 0;
                var searchTextCount = 0;
                var title = data.title.trim();
                var titleInLowerCase = title.toLowerCase();
                var content = data.content.trim().replace(/<[^>]+>/g,"");
                var contentInLowerCase = content.toLowerCase();
                var articleUrl = decodeURIComponent(data.url);
                var indexOfTitle = [];
                var indexOfContent = [];
                // only match articles with not empty titles
                if(title != '') {
                  keywords.forEach(function(keyword) {
                    function getIndexByWord(word, text, caseSensitive) {
                      var wordLen = word.length;
                      if (wordLen === 0) {
                        return [];
                      }
                      var startPosition = 0, position = [], index = [];
                      if (!caseSensitive) {
                        text = text.toLowerCase();
                        word = word.toLowerCase();
                      }
                      while ((position = text.indexOf(word, startPosition)) > -1) {
                        index.push({position: position, word: word});
                        startPosition = position + wordLen;
                      }
                      return index;
                    }

                    indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
                    indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
                  });
                  if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
                    isMatch = true;
                    hitCount = indexOfTitle.length + indexOfContent.length;
                  }
                }

                // show search results

                if (isMatch) {
                  // sort index by position of keyword

                  [indexOfTitle, indexOfContent].forEach(function (index) {
                    index.sort(function (itemLeft, itemRight) {
                      if (itemRight.position !== itemLeft.position) {
                        return itemRight.position - itemLeft.position;
                      } else {
                        return itemLeft.word.length - itemRight.word.length;
                      }
                    });
                  });

                  // merge hits into slices

                  function mergeIntoSlice(text, start, end, index) {
                    var item = index[index.length - 1];
                    var position = item.position;
                    var word = item.word;
                    var hits = [];
                    var searchTextCountInSlice = 0;
                    while (position + word.length <= end && index.length != 0) {
                      if (word === searchText) {
                        searchTextCountInSlice++;
                      }
                      hits.push({position: position, length: word.length});
                      var wordEnd = position + word.length;

                      // move to next position of hit

                      index.pop();
                      while (index.length != 0) {
                        item = index[index.length - 1];
                        position = item.position;
                        word = item.word;
                        if (wordEnd > position) {
                          index.pop();
                        } else {
                          break;
                        }
                      }
                    }
                    searchTextCount += searchTextCountInSlice;
                    return {
                      hits: hits,
                      start: start,
                      end: end,
                      searchTextCount: searchTextCountInSlice
                    };
                  }

                  var slicesOfTitle = [];
                  if (indexOfTitle.length != 0) {
                    slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
                  }

                  var slicesOfContent = [];
                  while (indexOfContent.length != 0) {
                    var item = indexOfContent[indexOfContent.length - 1];
                    var position = item.position;
                    var word = item.word;
                    // cut out 100 characters
                    var start = position - 20;
                    var end = position + 80;
                    if(start < 0){
                      start = 0;
                    }
                    if (end < position + word.length) {
                      end = position + word.length;
                    }
                    if(end > content.length){
                      end = content.length;
                    }
                    slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
                  }

                  // sort slices in content by search text's count and hits' count

                  slicesOfContent.sort(function (sliceLeft, sliceRight) {
                    if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
                      return sliceRight.searchTextCount - sliceLeft.searchTextCount;
                    } else if (sliceLeft.hits.length !== sliceRight.hits.length) {
                      return sliceRight.hits.length - sliceLeft.hits.length;
                    } else {
                      return sliceLeft.start - sliceRight.start;
                    }
                  });

                  // select top N slices in content

                  var upperBound = parseInt('1');
                  if (upperBound >= 0) {
                    slicesOfContent = slicesOfContent.slice(0, upperBound);
                  }

                  // highlight title and content

                  function highlightKeyword(text, slice) {
                    var result = '';
                    var prevEnd = slice.start;
                    slice.hits.forEach(function (hit) {
                      result += text.substring(prevEnd, hit.position);
                      var end = hit.position + hit.length;
                      result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
                      prevEnd = end;
                    });
                    result += text.substring(prevEnd, slice.end);
                    return result;
                  }

                  var resultItem = '';

                  if (slicesOfTitle.length != 0) {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
                  } else {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
                  }

                  slicesOfContent.forEach(function (slice) {
                    resultItem += "<a href='" + articleUrl + "'>" +
                      "<p class=\"search-result\">" + highlightKeyword(content, slice) +
                      "...</p>" + "</a>";
                  });

                  resultItem += "</li>";
                  resultItems.push({
                    item: resultItem,
                    searchTextCount: searchTextCount,
                    hitCount: hitCount,
                    id: resultItems.length
                  });
                }
              })
            };
            if (keywords.length === 1 && keywords[0] === "") {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
            } else if (resultItems.length === 0) {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
            } else {
              resultItems.sort(function (resultLeft, resultRight) {
                if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
                  return resultRight.searchTextCount - resultLeft.searchTextCount;
                } else if (resultLeft.hitCount !== resultRight.hitCount) {
                  return resultRight.hitCount - resultLeft.hitCount;
                } else {
                  return resultRight.id - resultLeft.id;
                }
              });
              var searchResultList = '<ul class=\"search-result-list\">';
              resultItems.forEach(function (result) {
                searchResultList += result.item;
              })
              searchResultList += "</ul>";
              resultContent.innerHTML = searchResultList;
            }
          }

          if ('auto' === 'auto') {
            input.addEventListener('input', inputEventFunction);
          } else {
            $('.search-icon').click(inputEventFunction);
            input.addEventListener('keypress', function (event) {
              if (event.keyCode === 13) {
                inputEventFunction();
              }
            });
          }

          // remove loading animation
          $(".local-search-pop-overlay").remove();
          $('body').css('overflow', '');

          proceedsearch();
        }
      });
    }

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched === false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };
    });

    $('.popup-btn-close').click(onPopupClose);
    $('.popup').click(function(e){
      e.stopPropagation();
    });
    $(document).on('keyup', function (event) {
      var shouldDismissSearchPopup = event.which === 27 &&
        $('.search-popup').is(':visible');
      if (shouldDismissSearchPopup) {
        onPopupClose();
      }
    });
  </script>





  

  

  

  
  

  

  

  

</body>
</html>
